diff --git a/src/Spring/Spring.Core/Core/IO/AbstractResource.cs b/src/Spring/Spring.Core/Core/IO/AbstractResource.cs index 8ac9ee46..7c496965 100644 --- a/src/Spring/Spring.Core/Core/IO/AbstractResource.cs +++ b/src/Spring/Spring.Core/Core/IO/AbstractResource.cs @@ -71,6 +71,7 @@ namespace Spring.Core.IO protected const string DefaultBasePathPlaceHolder = "~"; private string protocol; + private string resourceName; private string basePathPlaceHolder = DefaultBasePathPlaceHolder; #region Constructor (s) / Destructor @@ -109,7 +110,8 @@ namespace Spring.Core.IO protected AbstractResource(string resourceName) { AssertUtils.ArgumentHasText(resourceName, "resourceName"); - protocol = ConfigurableResourceLoader.GetProtocol(resourceName); + this.protocol = ConfigurableResourceLoader.GetProtocol(resourceName); + this.resourceName = resourceName; } #endregion @@ -196,6 +198,7 @@ namespace Spring.Core.IO get { return false; } } +#if !NET_2_0 /// /// Returns the handle for this resource. /// @@ -222,7 +225,19 @@ namespace Spring.Core.IO Description + " cannot be resolved to a Uri."); } } - +#else + /// + /// Returns the handle for this resource. + /// + /// + public virtual Uri Uri + { + get + { + return new Uri(resourceName); + } + } +#endif /// /// Returns a handle for this resource. /// diff --git a/src/Spring/Spring.Core/Core/IO/AssemblyResource.cs b/src/Spring/Spring.Core/Core/IO/AssemblyResource.cs index 8f4d8dd5..163c6028 100644 --- a/src/Spring/Spring.Core/Core/IO/AssemblyResource.cs +++ b/src/Spring/Spring.Core/Core/IO/AssemblyResource.cs @@ -248,6 +248,19 @@ namespace Spring.Core.IO } } +#if NET_2_0 + /// + /// Returns the handle for this resource. + /// + /// + public override Uri Uri + { + get + { + return new Uri(_fullResourceName); + } + } +#endif #endregion /// diff --git a/src/Spring/Spring.Core/Core/IO/ResourceHandlerRegistry.cs b/src/Spring/Spring.Core/Core/IO/ResourceHandlerRegistry.cs index bb3ab665..fcf3698c 100644 --- a/src/Spring/Spring.Core/Core/IO/ResourceHandlerRegistry.cs +++ b/src/Spring/Spring.Core/Core/IO/ResourceHandlerRegistry.cs @@ -110,14 +110,14 @@ namespace Spring.Core.IO { lock (resourceHandlers.SyncRoot) { - resourceHandlers["config"] = GetResourceConstructor(typeof(ConfigSectionResource)); - resourceHandlers["file"] = GetResourceConstructor(typeof(FileSystemResource)); - resourceHandlers["http"] = GetResourceConstructor(typeof(UrlResource)); - resourceHandlers["https"] = GetResourceConstructor(typeof(UrlResource)); + RegisterResourceHandler("config", typeof(ConfigSectionResource)); + RegisterResourceHandler("file", typeof(FileSystemResource)); + RegisterResourceHandler("http", typeof(UrlResource)); + RegisterResourceHandler("https", typeof(UrlResource)); #if NET_2_0 - resourceHandlers["ftp"] = GetResourceConstructor(typeof(UrlResource)); + RegisterResourceHandler("ftp", typeof(UrlResource)); #endif - resourceHandlers["assembly"] = GetResourceConstructor(typeof(AssemblyResource)); + RegisterResourceHandler("assembly", typeof(AssemblyResource)); // register custom resource handlers ConfigurationUtils.GetSection(ResourcesSectionName); @@ -244,11 +244,34 @@ namespace Spring.Core.IO lock (resourceHandlers.SyncRoot) { +#if NET_2_0 + // register generic uri parser for this scheme + if (!UriParser.IsKnownScheme(protocolName)) + { + UriParser.Register(new TolerantUriParser(), protocolName, 0); + } +#endif IDynamicConstructor ctor = GetResourceConstructor(handlerType); resourceHandlers[protocolName] = ctor; } } +#if NET_2_0 + /// + /// Allows to create any arbitrary Url format + /// + private class TolerantUriParser : GenericUriParser + { + private const GenericUriParserOptions DefaultOptions = GenericUriParserOptions.Default + |GenericUriParserOptions.GenericAuthority + |GenericUriParserOptions.AllowEmptyAuthority; + + public TolerantUriParser() + : base(DefaultOptions) + {} + } +#endif + private static IDynamicConstructor GetResourceConstructor(Type handlerType) { ConstructorInfo ctor = handlerType.GetConstructor(new Type[] {typeof(string)}); diff --git a/src/Spring/Spring.Core/Objects/Factory/Xml/NamespaceParserRegistry.cs b/src/Spring/Spring.Core/Objects/Factory/Xml/NamespaceParserRegistry.cs index cc8d7d0a..73caf7af 100644 --- a/src/Spring/Spring.Core/Objects/Factory/Xml/NamespaceParserRegistry.cs +++ b/src/Spring/Spring.Core/Objects/Factory/Xml/NamespaceParserRegistry.cs @@ -47,6 +47,26 @@ namespace Spring.Objects.Factory.Xml /// Aleksandar Seovic public class NamespaceParserRegistry { + /// + /// Resolves xml entities by using the infrastructure. + /// + private class XmlResourceUrlResolver : XmlUrlResolver + { + public override object GetEntity( Uri absoluteUri, string role, Type ofObjectToReturn ) + { + IResourceLoader resourceLoader = new ConfigurableResourceLoader(); + IResource resource = resourceLoader.GetResource(absoluteUri.AbsoluteUri); + return resource.InputStream; + //return base.GetEntity( absoluteUri, role, ofObjectToReturn ); + } + + public override Uri ResolveUri( Uri baseUri, string relativeUri ) + { + // TODO: resolve Uri using IResource instance + return base.ResolveUri( baseUri, relativeUri ); + } + } + /// /// Name of the .Net config section that contains definitions /// for custom config parsers. @@ -55,12 +75,12 @@ namespace Spring.Objects.Factory.Xml #region Fields - private static IDictionary parsers = new HybridDictionary(); + private readonly static IDictionary parsers; #if !NET_2_0 - private static XmlSchemaCollection schemas = new XmlSchemaCollection(); + private readonly static XmlSchemaCollection schemas; #else - private static XmlSchemaSet schemas = new XmlSchemaSet(); + private readonly static XmlSchemaSet schemas; #endif #endregion @@ -70,19 +90,23 @@ namespace Spring.Objects.Factory.Xml /// 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); + parsers = new HybridDictionary(); +#if !NET_2_0 + schemas = new XmlSchemaCollection(); +#else + schemas = new XmlSchemaSet(); + schemas.XmlResolver = new XmlResourceUrlResolver(); +#endif - // register custom config parsers - ConfigurationUtils.GetSection(ConfigParsersSectionName); - } + //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); } /// @@ -280,9 +304,20 @@ namespace Spring.Objects.Factory.Xml { IResourceLoader resourceLoader = new ConfigurableResourceLoader(); IResource schema = resourceLoader.GetResource(schemaLocation); - try { - schemas.Add(namespaceUri, new XmlTextReader(schema.InputStream)); - } catch (Exception e) + try + { +#if NET_1_0 + XmlTextReader schemaDocument = new XmlTextReader(schemaLocation, schema.InputStream); + schemas.Add(namespaceUri, schemaDocument); +#elif NET_1_1 + XmlTextReader schemaDocument = new XmlTextReader(schemaLocation, schema.InputStream); + schemas.Add(namespaceUri, schemaDocument, new XmlResourceUrlResolver()); +#else + XmlTextReader schemaDocument = new XmlTextReader(schema.Uri.AbsoluteUri, schema.InputStream); + schemas.Add(namespaceUri, schemaDocument); +#endif + } + catch (Exception e) { throw new ArgumentException("Could not load schema from resource = " + schema, e); } diff --git a/test/Spring/Spring.Core.Tests/Objects/Factory/Xml/NamespaceParserRegistryTests.cs b/test/Spring/Spring.Core.Tests/Objects/Factory/Xml/NamespaceParserRegistryTests.cs new file mode 100644 index 00000000..ddf412f3 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Objects/Factory/Xml/NamespaceParserRegistryTests.cs @@ -0,0 +1,82 @@ +#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.Xml; +using System.Xml.Schema; +using NUnit.Framework; +using Spring.Core.IO; +using Spring.Objects.Factory.Config; +using Spring.Util; + +#endregion + +namespace Spring.Objects.Factory.Xml +{ + /// + /// + /// + /// Erich Eichinger + [TestFixture] + public class NamespaceParserRegistryTests + { + private class TestNamespaceParser : INamespaceParser + { + public void Init() + { + } + + public IObjectDefinition ParseElement(XmlElement element, ParserContext parserContext) + { + throw new System.NotImplementedException(); + } + + public ObjectDefinitionHolder Decorate(XmlNode node, ObjectDefinitionHolder definition, ParserContext parserContext) + { + throw new System.NotImplementedException(); + } + } + +#if !NET_1_0 + /// + /// This test doesn't work on .NET 1.0 because there are no XmlResolvers for schema loading... + /// + [Test] + public void CanLoadSchemaImportingOtherSchemaByRelativePath() + { + string schemaLocation = GetAssemblyResource( this.GetType(), "NamespaceParserRegistryTests_TestSchema.xsd" ); + NamespaceParserRegistry.RegisterParser(new TestNamespaceParser(), "http://www.example.com/brief", schemaLocation); + XmlReader vr = XmlUtils.CreateValidatingReader( new StringResource( + @" + + ").InputStream, NamespaceParserRegistry.GetSchemas(), null); + ConfigXmlDocument newDoc = new ConfigXmlDocument(); + newDoc.Load(vr); + } +#endif + private string GetAssemblyResource( Type hint, string name) + { + return "assembly://" + hint.Assembly.FullName.Split(',')[0].Trim() + "/" + hint.Namespace + "/" + name; + } + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Objects/Factory/Xml/NamespaceParserRegistryTests_TestSchema.xsd b/test/Spring/Spring.Core.Tests/Objects/Factory/Xml/NamespaceParserRegistryTests_TestSchema.xsd new file mode 100644 index 00000000..3eb08fdb --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Objects/Factory/Xml/NamespaceParserRegistryTests_TestSchema.xsd @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + diff --git a/test/Spring/Spring.Core.Tests/Objects/Factory/Xml/NamespaceParserRegistryTests_TestSchema_2.xsd b/test/Spring/Spring.Core.Tests/Objects/Factory/Xml/NamespaceParserRegistryTests_TestSchema_2.xsd new file mode 100644 index 00000000..650f07f7 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Objects/Factory/Xml/NamespaceParserRegistryTests_TestSchema_2.xsd @@ -0,0 +1,11 @@ + + + + + + + + diff --git a/test/Spring/Spring.Core.Tests/Spring.Core.Tests.2003.csproj b/test/Spring/Spring.Core.Tests/Spring.Core.Tests.2003.csproj index 204c0622..9e69e8e6 100644 --- a/test/Spring/Spring.Core.Tests/Spring.Core.Tests.2003.csproj +++ b/test/Spring/Spring.Core.Tests/Spring.Core.Tests.2003.csproj @@ -1592,6 +1592,19 @@ SubType = "Code" BuildAction = "Compile" /> + + + + + @@ -748,6 +750,8 @@ testobject.xsd + + @@ -889,4 +893,4 @@ xcopy "$(ProjectDir)Data" ..\..\..\..\build\VS.Net.2008\Spring.Core.Tests\$(ConfigurationName)\ /y /s /q /d xcopy "$(ProjectDir)$(TargetFileName).config" ..\..\..\..\build\VS.Net.2008\Spring.Core.Tests\$(ConfigurationName)\ /y /s /q - + \ No newline at end of file