diff --git a/BreakingChanges-1.2.txt b/BreakingChanges-1.2.txt index 2d9e21a5..4f9c4610 100644 --- a/BreakingChanges-1.2.txt +++ b/BreakingChanges-1.2.txt @@ -17,6 +17,8 @@ Spring.Core +2. XmlReaderContext constructor now requires an IObjectDefinitionFactory to be specified. Thus XmlReaderContext.ObjectDefinitionFactory + is read only now. Changes (1.1.2 to 1.2 RC1 or greater) diff --git a/src/Spring/Spring.Core/Context/Support/AbstractXmlApplicationContext.cs b/src/Spring/Spring.Core/Context/Support/AbstractXmlApplicationContext.cs index 860279fe..6be4cf89 100644 --- a/src/Spring/Spring.Core/Context/Support/AbstractXmlApplicationContext.cs +++ b/src/Spring/Spring.Core/Context/Support/AbstractXmlApplicationContext.cs @@ -226,7 +226,7 @@ namespace Spring.Context.Support protected virtual void LoadObjectDefinitions(DefaultListableObjectFactory objectFactory) { //Create a new XmlObjectDefinitionReader for the given ObjectFactory - XmlObjectDefinitionReader objectDefinitionReader = new XmlObjectDefinitionReader(objectFactory); + XmlObjectDefinitionReader objectDefinitionReader = CreateXmlObjectDefinitionReader(objectFactory); // Configure the bean definition reader with this context's // resource loading environment. @@ -236,6 +236,16 @@ namespace Spring.Context.Support // then proceed with actually loading the object definitions. InitObjectDefinitionReader(objectDefinitionReader); LoadObjectDefinitions(objectDefinitionReader); + } + + /// + /// Create a new reader instance for importing object definitions into the specified . + /// + /// the to be associated with the reader + /// a new instance. + protected virtual XmlObjectDefinitionReader CreateXmlObjectDefinitionReader(DefaultListableObjectFactory objectFactory) + { + return new XmlObjectDefinitionReader(objectFactory); } /// diff --git a/src/Spring/Spring.Core/Context/Support/GenericApplicationContext.cs b/src/Spring/Spring.Core/Context/Support/GenericApplicationContext.cs index eeb91b87..cfeee781 100644 --- a/src/Spring/Spring.Core/Context/Support/GenericApplicationContext.cs +++ b/src/Spring/Spring.Core/Context/Support/GenericApplicationContext.cs @@ -251,6 +251,16 @@ namespace Spring.Context.Support public void RegisterAlias(string name, string theAlias) { objectFactory.RegisterAlias(name, theAlias); + } + + /// + /// Determines whether the given object name is already in use within this factory, + /// i.e. whether there is a local object or alias registered under this name or + /// an inner object created with this name. + /// + public bool IsObjectNameInUse(string objectName) + { + return objectFactory.IsObjectNameInUse(objectName); } #endregion diff --git a/src/Spring/Spring.Core/Objects/Factory/Support/AbstractObjectDefinitionReader.cs b/src/Spring/Spring.Core/Objects/Factory/Support/AbstractObjectDefinitionReader.cs index 1ce7974e..5e6ac00c 100644 --- a/src/Spring/Spring.Core/Objects/Factory/Support/AbstractObjectDefinitionReader.cs +++ b/src/Spring/Spring.Core/Objects/Factory/Support/AbstractObjectDefinitionReader.cs @@ -232,7 +232,7 @@ namespace Spring.Objects.Factory.Support if (log.IsDebugEnabled) { - log.Debug("Loaded " + loadCount + " bean definitions from location [" + location + "]"); + log.Debug("Loaded " + loadCount + " object definitions from location [" + location + "]"); } return loadCount; } diff --git a/src/Spring/Spring.Core/Objects/Factory/Support/IObjectDefinitionRegistry.cs b/src/Spring/Spring.Core/Objects/Factory/Support/IObjectDefinitionRegistry.cs index aa5c8915..da22027b 100644 --- a/src/Spring/Spring.Core/Objects/Factory/Support/IObjectDefinitionRegistry.cs +++ b/src/Spring/Spring.Core/Objects/Factory/Support/IObjectDefinitionRegistry.cs @@ -45,6 +45,12 @@ namespace Spring.Objects.Factory.Support /// Rick Evans (.NET) public interface IObjectDefinitionRegistry { + /// + /// Determine whether the given object name is already in use within this registry, + /// i.e. whether there is a local object or alias registered under this name. + /// + bool IsObjectNameInUse(string objectName); + /// /// Return the number of objects defined in the registry. /// diff --git a/src/Spring/Spring.Core/Objects/Factory/Xml/DefaultNamespaceHandlerResolver.cs b/src/Spring/Spring.Core/Objects/Factory/Xml/DefaultNamespaceHandlerResolver.cs new file mode 100644 index 00000000..4d4d1836 --- /dev/null +++ b/src/Spring/Spring.Core/Objects/Factory/Xml/DefaultNamespaceHandlerResolver.cs @@ -0,0 +1,43 @@ +#region License + +/* + * Copyright 2002-2009 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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 +{ + /// + /// Default implementation of the interface. + /// Resolves namespace URIs to implementation types based on mappings. + /// + /// Erich Eichinger + /// + /// + internal class DefaultNamespaceHandlerResolver : INamespaceParserResolver + { + /// + /// Resolve the namespace URI and return the corresponding + /// implementation. + /// + /// the namespace URI to get the matching parser for. + /// the matching parser or null + public INamespaceParser Resolve(string namespaceUri) + { + return NamespaceParserRegistry.GetParser(namespaceUri); + } + } +} \ 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 index db1f0ef5..3561b8e3 100644 --- a/src/Spring/Spring.Core/Objects/Factory/Xml/DefaultObjectDefinitionDocumentReader.cs +++ b/src/Spring/Spring.Core/Objects/Factory/Xml/DefaultObjectDefinitionDocumentReader.cs @@ -352,8 +352,7 @@ namespace Spring.Objects.Factory.Xml /// a new instance protected virtual ObjectDefinitionParserHelper CreateHelper(XmlReaderContext readerContext, XmlElement root) { - ObjectDefinitionParserHelper helper = new ObjectDefinitionParserHelper(readerContext); - helper.InitDefaults(root); + ObjectDefinitionParserHelper helper = new ObjectDefinitionParserHelper(readerContext, root); return helper; } diff --git a/src/Spring/Spring.Core/Objects/Factory/Xml/INamespaceParser.cs b/src/Spring/Spring.Core/Objects/Factory/Xml/INamespaceParser.cs index 794e624a..98ade10d 100644 --- a/src/Spring/Spring.Core/Objects/Factory/Xml/INamespaceParser.cs +++ b/src/Spring/Spring.Core/Objects/Factory/Xml/INamespaceParser.cs @@ -30,7 +30,7 @@ namespace Spring.Objects.Factory.Xml { /// - /// Strategy interface for parsing XML object definitions. + /// Strategy interface for parsing XML object definitions. Equivalent to Spring/Java's NamespaceHandler interface. /// /// ///

@@ -38,7 +38,7 @@ namespace Spring.Objects.Factory.Xml /// for actually parsing a DOM document or /// fragment. ///

- ///
+ /// /// Juergen Hoeller /// Rick Evans (.NET) /// Sandu Turcan (.NET) diff --git a/src/Spring/Spring.Core/Objects/Factory/Xml/INamespaceParserResolver.cs b/src/Spring/Spring.Core/Objects/Factory/Xml/INamespaceParserResolver.cs new file mode 100644 index 00000000..622a536d --- /dev/null +++ b/src/Spring/Spring.Core/Objects/Factory/Xml/INamespaceParserResolver.cs @@ -0,0 +1,43 @@ +#region License + +/* + * Copyright 2002-2009 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS 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.Xml +{ + /// + /// Used by to locate + /// implementations for a particular namespace URI. + /// + /// TODO (EE): clarify naming of INamespaceParser (SPR/NET) vs. INamespaceHandler (SPR/Java), thus internal for now + /// Erich Eichinger + /// + /// + /// + internal interface INamespaceParserResolver + { + /// + /// Lookup a for the given namespace URI. + /// + /// the namespace URI + /// the located namespace handler or null + INamespaceParser Resolve(string namespaceUri); + } +} \ 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 index ff8a9f18..22c080e5 100644 --- a/src/Spring/Spring.Core/Objects/Factory/Xml/ObjectDefinitionParserHelper.cs +++ b/src/Spring/Spring.Core/Objects/Factory/Xml/ObjectDefinitionParserHelper.cs @@ -1,5 +1,5 @@ -#region License - +#region License + /* * Copyright © 2002-2007 the original author or authors. * @@ -14,163 +14,182 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - */ - -#endregion - -using System; + */ + +#endregion + +using System; using System.Collections; -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 -{ - /// - /// Stateful class used to parse XML object definitions. - /// +using System.Globalization; +using System.Reflection; +using System.Xml; +using Common.Logging; +using Spring.Collections; +using Spring.Objects.Factory.Config; +using Spring.Objects.Factory.Support; +using Spring.Util; + +namespace Spring.Objects.Factory.Xml +{ + /// + /// Stateful class used to parse XML object definitions. + /// /// Not all parsing code has been refactored into this class. See - /// BeanDefinitionParserDelegate in Java for how this class should evolve. - /// Rob Harrop - /// Juergen Hoeller - /// Rod Johnson - /// Mark Pollack (.NET) - 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 = GetAttributeValue(root, ObjectDefinitionConstants.DefaultLazyInitAttribute); - - #region Instrumentation - - if (log.IsDebugEnabled) - { - log.Debug( - string.Format( - "Default lazy init '{0}'.", - ddd.LazyInit)); - } - + /// BeanDefinitionParserDelegate in Java for how this class should evolve. + /// Rob Harrop + /// Juergen Hoeller + /// Rod Johnson + /// Mark Pollack (.NET) + public class ObjectDefinitionParserHelper + { + #region Fields + + /// + /// The shared instance for this class (and derived classes). + /// + protected readonly ILog log; + + private DocumentDefaultsDefinition defaults; + + private readonly XmlReaderContext readerContext; + + private readonly ObjectsNamespaceParser objectsNamespaceParser; + + private readonly ISet usedNames = new HashedSet(); + + #endregion + + /// + /// Initializes a new instance of the class. + /// + /// The reader context. + public ObjectDefinitionParserHelper(XmlReaderContext readerContext) + :this(readerContext, null) + {} + + /// + /// Initializes a new instance of the class. + /// + /// The reader context. + /// The root element of the definition document to parse + public ObjectDefinitionParserHelper(XmlReaderContext readerContext, XmlElement root) + { + log = LogManager.GetLogger(this.GetType()); + this.readerContext = readerContext; + this.objectsNamespaceParser = (ObjectsNamespaceParser) readerContext.NamespaceParserResolver.Resolve(ObjectsNamespaceParser.Namespace); + if (root != null) + { + InitDefaults(root); + } + } + + /// + /// 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.DependencyCheck = GetAttributeValue(root, ObjectDefinitionConstants.DefaultDependencyCheckAttribute); - - #region Instrumentation - - if (log.IsDebugEnabled) - { - log.Debug( - string.Format( - "Default dependency check '{0}'.", - ddd.DependencyCheck)); - } - + ddd.LazyInit = GetAttributeValue(root, ObjectDefinitionConstants.DefaultLazyInitAttribute); + + #region Instrumentation + + if (log.IsDebugEnabled) + { + log.Debug( + string.Format( + "Default lazy init '{0}'.", + ddd.LazyInit)); + } + #endregion - ddd.Autowire = GetAttributeValue(root, 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; + ddd.DependencyCheck = GetAttributeValue(root, ObjectDefinitionConstants.DefaultDependencyCheckAttribute); + + #region Instrumentation + + if (log.IsDebugEnabled) + { + log.Debug( + string.Format( + "Default dependency check '{0}'.", + ddd.DependencyCheck)); + } + + #endregion + + ddd.Autowire = GetAttributeValue(root, 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; } /// @@ -194,7 +213,7 @@ namespace Spring.Objects.Factory.Xml /// as the canonical name, registering all others as aliases. /// /// - public ObjectDefinitionHolder ParseObjectDefinitionElement(XmlElement element) + public ObjectDefinitionHolder ParseObjectDefinitionElement(XmlElement element) { return ParseObjectDefinitionElement(element, null); } @@ -221,11 +240,8 @@ namespace Spring.Objects.Factory.Xml /// as the canonical name, registering all others as aliases. /// /// - public ObjectDefinitionHolder ParseObjectDefinitionElement(XmlElement element, IObjectDefinition containingDefinition) + public ObjectDefinitionHolder ParseObjectDefinitionElement(XmlElement element, IObjectDefinition containingDefinition) { - // TODO: move code from ObjectsNamespaceParser into this class to eliminate ONP - ObjectsNamespaceParser parser = (ObjectsNamespaceParser) NamespaceParserRegistry.GetParser(ObjectsNamespaceParser.Namespace); - string id = GetAttributeValue(element, ObjectDefinitionConstants.IdAttribute); string nameAttr = GetAttributeValue(element, ObjectDefinitionConstants.NameAttribute); ArrayList aliases = new ArrayList(); @@ -238,13 +254,26 @@ namespace Spring.Objects.Factory.Xml string objectName = id; if (StringUtils.IsNullOrEmpty(objectName)) { - // TODO (EE): pass parserContext to CalculateId as well (resolving relative Urls in WebApps is parserContext-dependent) (EE) - objectName = parser.CalculateId(element, aliases); + if (aliases.Count > 0) + { + objectName = (string) aliases[0]; + aliases.RemoveAt(0); + if (log.IsDebugEnabled) + { + log.Debug(string.Format("No XML 'id' specified using '{0}' as object name and '{1}' as aliases", objectName, string.Join(",", (string[]) aliases.ToArray(typeof(string))))); + } + } } + objectName = PostProcessObjectNameAndAliases(objectName, aliases, element, containingDefinition); + + if (containingDefinition == null) + { + CheckNameUniqueness(objectName, aliases, element); + } ParserContext parserContext = new ParserContext(this, containingDefinition); - IConfigurableObjectDefinition definition = parser.ParseObjectDefinitionElement(element, objectName, parserContext); + IConfigurableObjectDefinition definition = objectsNamespaceParser.ParseObjectDefinitionElement(element, objectName, parserContext); if (definition != null) { if (StringUtils.IsNullOrEmpty(objectName)) @@ -252,11 +281,20 @@ namespace Spring.Objects.Factory.Xml if (containingDefinition != null) { objectName = - ObjectDefinitionReaderUtils.GenerateObjectName(definition, parserContext.Registry, true); + ObjectDefinitionReaderUtils.GenerateObjectName(definition, readerContext.Registry, true); } else { - objectName = ObjectDefinitionReaderUtils.GenerateObjectName(definition, parserContext.Registry); + objectName = readerContext.GenerateObjectName(definition); + // Register an alias for the plain object type name, if possible. + string objectTypeName = definition.ObjectTypeName; + if (objectTypeName != null + && objectName.StartsWith(objectTypeName) + && objectName.Length>objectTypeName.Length + && !readerContext.Registry.IsObjectNameInUse(objectTypeName)) + { + aliases.Add(objectTypeName); + } } #region Instrumentation @@ -264,17 +302,76 @@ namespace Spring.Objects.Factory.Xml 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)); + "Neither XML '{0}' nor '{1}' specified - using generated object name [{2}]", + ObjectDefinitionConstants.IdAttribute, ObjectDefinitionConstants.NameAttribute, objectName)); } #endregion } string[] aliasesArray = (string[])aliases.ToArray(typeof(string)); - return new ObjectDefinitionHolder(definition, objectName, aliasesArray); + return CreateObjectDefinitionHolder(element, definition, objectName, aliasesArray); } - return null; + return null; + } + + /// + /// Create an instance from the given and . + /// + /// + /// This method may be used as a last resort to post-process an object definition before it gets added to the registry. + /// + protected virtual ObjectDefinitionHolder CreateObjectDefinitionHolder(XmlElement element, IConfigurableObjectDefinition definition, string objectName, string[] aliasesArray) + { + return new ObjectDefinitionHolder(definition, objectName, aliasesArray); + } + + /// + /// Allows deriving classes to post process the name and aliases for the current element. By default + /// does nothing and returns the unmodified . + /// + /// + /// The list passed in may be modified by an implementation of this method to reflect special needs. + /// + /// the object name obtained by the default algorithm from 'id' and 'name' attributes so far. + /// the object aliases obtained by the default algorithm from 'name' attribute so far. + /// the currently processed element. + /// the containing object definition, may be null + /// the new object name to be used. + protected virtual string PostProcessObjectNameAndAliases(string objectName, ArrayList aliases, XmlElement element, IObjectDefinition containingDefinition) + { + if (!StringUtils.HasText(objectName) && aliases.Count == 0) + { + string result = this.objectsNamespaceParser.CalculateId(element, aliases); + if (result != null) + { + return result; + } + } + return objectName; + } + + /// + /// Validate that the specified object name and aliases have not been used already. + /// + protected virtual void CheckNameUniqueness(string objectName, ArrayList aliases, XmlElement element) + { + string foundName = null; + + if (StringUtils.HasText(objectName) && this.usedNames.Contains(objectName)) + { + foundName = objectName; + } + if (foundName == null) + { + foundName = (string) CollectionUtils.FindFirstMatch(this.usedNames, aliases); + } + if(foundName != null) + { + Error("Object name '" + foundName + "' is already used in this file", element); + } + + this.usedNames.Add(objectName); + this.usedNames.AddAll(aliases); } /// @@ -321,37 +418,37 @@ namespace Spring.Objects.Factory.Xml return StringUtils.Split( value, ObjectDefinitionConstants.ObjectNameDelimiters, true, true); } - - /// - /// 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); + + /// + /// 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); } /// @@ -385,14 +482,14 @@ namespace Spring.Objects.Factory.Xml return element.GetAttribute(attributeName); } return defaultValue; - } - + } + /// /// Report a parser error. /// - protected virtual void Error(string message, XmlElement element) + protected virtual void Error(string message, XmlElement element) { this.ReaderContext.ReportFatalException(element, message); - } - } -} + } + } +} diff --git a/src/Spring/Spring.Core/Objects/Factory/Xml/ObjectsNamespaceParser.cs b/src/Spring/Spring.Core/Objects/Factory/Xml/ObjectsNamespaceParser.cs index a7838852..c21db336 100644 --- a/src/Spring/Spring.Core/Objects/Factory/Xml/ObjectsNamespaceParser.cs +++ b/src/Spring/Spring.Core/Objects/Factory/Xml/ObjectsNamespaceParser.cs @@ -384,54 +384,7 @@ namespace Spring.Objects.Factory.Xml [Obsolete("not used anymore - ObjectsNamespaceParser will be dropped with 2.x, use ObjectDefinitionParserHelper instead", false)] protected ObjectDefinitionHolder ParseObjectDefinitionElement(XmlElement element, ParserContext parserContext, bool nestedDefinition) { - string id = GetAttributeValue(element, ObjectDefinitionConstants.IdAttribute); - string nameAttr = GetAttributeValue(element, ObjectDefinitionConstants.NameAttribute); - ArrayList aliases = new ArrayList(); - if (StringUtils.HasText(nameAttr)) - { - aliases.AddRange(GetObjectNames(nameAttr)); - } - - // if we ain't got an id, check if object is page definition or assign any existing (first) alias... - string objectName = id; - if (StringUtils.IsNullOrEmpty(objectName)) - { - // TODO (EE): pass parserContext to CalculateId as well (resolving relative Urls in WebApps is parserContext-dependent) (EE) - objectName = CalculateId(element, aliases); - } - - - IConfigurableObjectDefinition definition = ParseObjectDefinitionElement(element, objectName, parserContext); - if (definition != null) - { - if (StringUtils.IsNullOrEmpty(objectName)) - { - if (nestedDefinition) - { - objectName = - ObjectDefinitionReaderUtils.GenerateObjectName(definition, parserContext.Registry, true); - } - else - { - objectName = 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, objectName, aliasesArray); - } - return null; + return parserContext.ParserHelper.ParseObjectDefinitionElement(element, parserContext.ContainingObjectDefinition); } /// @@ -453,32 +406,10 @@ namespace Spring.Objects.Factory.Xml /// /// A calculated object definition id. /// + [Obsolete("This method will be dropped, override ObjectDefinitionParserHelper.PostProcessObjectNameAndAliases instead", false)] protected internal 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; + return null; } /// @@ -514,12 +445,12 @@ namespace Spring.Objects.Factory.Xml = parserContext.ReaderContext.ObjectDefinitionFactory.CreateObjectDefinition( typeName, parent, parserContext.ReaderContext.Reader.Domain); + ParserContext childParserContext = new ParserContext(parserContext.ParserHelper, od); - MutablePropertyValues pvs = ParsePropertyElements(id, element, parserContext); - ConstructorArgumentValues arguments - = ParseConstructorArgSubElements(id, element, parserContext); - EventValues events = ParseEventHandlerSubElements(id, element, parserContext); - MethodOverrides methodOverrides = ParseMethodOverrideSubElements(id, element, parserContext); + MutablePropertyValues pvs = ParsePropertyElements(id, element, childParserContext); + ConstructorArgumentValues arguments = ParseConstructorArgSubElements(id, element, childParserContext); + EventValues events = ParseEventHandlerSubElements(id, element, childParserContext); + MethodOverrides methodOverrides = ParseMethodOverrideSubElements(id, element, childParserContext); bool isPage = StringUtils.HasText(typeName) && typeName != null && typeName.ToLower().EndsWith(".aspx"); if (!isPage) @@ -540,13 +471,13 @@ namespace Spring.Objects.Factory.Xml string dependencyCheck = GetAttributeValue(element, ObjectDefinitionConstants.DependencyCheckAttribute); if (ObjectDefinitionConstants.DefaultValue.Equals(dependencyCheck)) { - dependencyCheck = parserContext.ParserHelper.Defaults.DependencyCheck; + dependencyCheck = childParserContext.ParserHelper.Defaults.DependencyCheck; } od.DependencyCheck = GetDependencyCheck(dependencyCheck); string autowire = GetAttributeValue(element, ObjectDefinitionConstants.AutowireAttribute); if (ObjectDefinitionConstants.DefaultValue.Equals(autowire)) { - autowire = parserContext.ParserHelper.Defaults.Autowire; + autowire = childParserContext.ParserHelper.Defaults.Autowire; } od.AutowireMode = GetAutowireMode(autowire); string initMethodName = GetAttributeValue(element, ObjectDefinitionConstants.InitMethodAttribute); @@ -567,12 +498,12 @@ namespace Spring.Objects.Factory.Xml if (ObjectDefinitionConstants.DefaultValue.Equals(lazyInit) && od.IsSingleton) { // just apply default to singletons, as lazy-init has no meaning for prototypes... - lazyInit = parserContext.ParserHelper.Defaults.LazyInit; + lazyInit = childParserContext.ParserHelper.Defaults.LazyInit; } od.IsLazyInit = IsTrueStringValue(lazyInit); // try to get the line info - string resourceDescription = parserContext.ParserHelper.ReaderContext.Resource.Description; + string resourceDescription = childParserContext.ParserHelper.ReaderContext.Resource.Description; if (StringUtils.HasText(resourceDescription)) { int line = ConfigurationUtils.GetLineNumber(element); @@ -940,7 +871,7 @@ namespace Spring.Objects.Factory.Xml { case ObjectDefinitionConstants.ObjectElement: { - return ParseObjectDefinitionElement(element, parserContext, true); + return parserContext.ParserHelper.ParseObjectDefinitionElement(element, parserContext.ContainingObjectDefinition); } case ObjectDefinitionConstants.RefElement: { @@ -986,35 +917,38 @@ namespace Spring.Objects.Factory.Xml "Unknown subelement of : <" + element.Name + ">"); } } - - // 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(parserContext.ParserHelper)); - } - throw new ObjectDefinitionStoreException( - parserContext.ReaderContext.Resource, - name, - "Unknown subelement of : <" + element.Name + ">"); + return parserContext.ParserHelper.ParseCustomElement(element, parserContext.ContainingObjectDefinition); + +// parserContext.ParserHelper.parse +// // it may match another Parser +// INamespaceParser otherParser = parserContext. (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(parserContext.ParserHelper)); +// } +// +// throw new ObjectDefinitionStoreException( +// parserContext.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 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 ParseIdReference(XmlElement element, ObjectDefinitionParserHelper parserHelper, string name) { diff --git a/src/Spring/Spring.Core/Objects/Factory/Xml/XmlObjectDefinitionReader.cs b/src/Spring/Spring.Core/Objects/Factory/Xml/XmlObjectDefinitionReader.cs index 87900434..0817845e 100644 --- a/src/Spring/Spring.Core/Objects/Factory/Xml/XmlObjectDefinitionReader.cs +++ b/src/Spring/Spring.Core/Objects/Factory/Xml/XmlObjectDefinitionReader.cs @@ -1,5 +1,5 @@ -#region License - +#region License + /* * Copyright 2002-2005 the original author or authors. * @@ -14,56 +14,57 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 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) - public class XmlObjectDefinitionReader : AbstractObjectDefinitionReader - { + */ + +#endregion + +#region Imports + +using System; +using System.IO; +using System.Xml; +using System.Xml.Schema; +using Spring.Core.IO; +using Spring.Objects.Factory.Config; +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) + public class XmlObjectDefinitionReader : AbstractObjectDefinitionReader + { #region Utility Classes /// /// For retrying the parse process - /// - private class RetryParseException : Exception + ///
+ private class RetryParseException : Exception { public RetryParseException() - {} - } - + { } + } + #if !NET_2_0 private class ValidationEventHandlerWrapper { @@ -85,208 +86,266 @@ namespace Spring.Objects.Factory.Xml _owner.HandleValidation(_sender,args); } } -#endif +#endif #endregion - - #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 - { - // create local copy of data - byte[] xmlData = IOUtils.ToByteArray( stream ); + + #region Fields + + [NonSerialized] + private XmlResolver resolver; + + private Type documentReaderType; + private INamespaceParserResolver namespaceParserResolver; + private IObjectDefinitionFactory objectDefinitionFactory; + + #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) + : this(registry, resolver, new DefaultObjectDefinitionFactory()) + { + Resolver = resolver; + } + + /// + /// Creates a new instance of the + /// class. + /// + /// + /// The + /// instance that this reader works on. + /// + /// + /// The to be used for parsing. + /// + /// the to use for creating new s + protected XmlObjectDefinitionReader(IObjectDefinitionRegistry registry, XmlResolver resolver, IObjectDefinitionFactory objectDefinitionFactory) + : base(registry) + { + Resolver = resolver; + this.objectDefinitionFactory = objectDefinitionFactory; + } + + #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; + } + } + + /// + /// Specify a to use. If none is specified a default + /// instance will be created by + /// + internal INamespaceParserResolver NamespaceParserResolver + { + get + { + if (this.namespaceParserResolver == null) + { + this.namespaceParserResolver = CreateDefaultNamespaceParserResolver(); + } + return this.namespaceParserResolver; + } + set + { + if (this.namespaceParserResolver != null) + { + throw new InvalidOperationException("NamespaceParserResolver is already set"); + } + this.namespaceParserResolver = value; + } + } + + /// + /// Specify a for creating instances of . + /// + protected IObjectDefinitionFactory ObjectDefinitionFactory + { + get + { + return this.objectDefinitionFactory; + } + } + + #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 + { + // create local copy of data + byte[] xmlData = IOUtils.ToByteArray(stream); XmlDocument doc; // loop until no unregistered, wellknown namespaces left - while(true) + while (true) { XmlReader reader = null; try { MemoryStream xmlDataStream = new MemoryStream(xmlData); - reader = CreateValidatingReader(xmlDataStream); - doc = new ConfigXmlDocument(); - doc.Load(reader); - break; + reader = CreateValidatingReader(xmlDataStream); + doc = new ConfigXmlDocument(); + doc.Load(reader); + break; } - catch(RetryParseException) + catch (RetryParseException) { - if (reader != null) reader.Close(); + if (reader != null) + reader.Close(); } } - return RegisterObjectDefinitions(doc, resource); - } - catch (XmlException ex) - { - throw new ObjectDefinitionStoreException(resource.Description, - "Line " + ex.LineNumber + " in XML document from " + - resource + " is not well formed. " + ex.Message, ex); - } - catch (XmlSchemaException ex) - { - throw new ObjectDefinitionStoreException(resource.Description, - "Line " + ex.LineNumber + " in XML document from " + - resource + " violates the schema. " + ex.Message, ex); - } - catch(ObjectDefinitionStoreException) - { - throw; - } - catch (Exception ex) - { - throw new ObjectDefinitionStoreException("Unexpected exception parsing XML document from " + resource.Description + "Inner exception message= " + ex.Message, ex); - } + return RegisterObjectDefinitions(doc, resource); + } + catch (XmlException ex) + { + throw new ObjectDefinitionStoreException(resource.Description, + "Line " + ex.LineNumber + " in XML document from " + + resource + " is not well formed. " + ex.Message, ex); + } + catch (XmlSchemaException ex) + { + throw new ObjectDefinitionStoreException(resource.Description, + "Line " + ex.LineNumber + " in XML document from " + + resource + " violates the schema. " + ex.Message, ex); + } + catch (ObjectDefinitionStoreException) + { + throw; + } + catch (Exception ex) + { + throw new ObjectDefinitionStoreException("Unexpected exception parsing XML document from " + resource.Description + "Inner exception message= " + ex.Message, ex); + } } private XmlReader CreateValidatingReader(MemoryStream stream) @@ -297,7 +356,7 @@ namespace Spring.Objects.Factory.Xml reader = XmlUtils.CreateReader(stream); } else - { + { #if !NET_2_0 // only because 1.0/1.1 don't pass the sender into the handler callback... ValidationEventHandlerWrapper validationEventHandlerWrapper = new ValidationEventHandlerWrapper(this); @@ -305,116 +364,134 @@ namespace Spring.Objects.Factory.Xml new ValidationEventHandler(validationEventHandlerWrapper.HandleValidation)); validationEventHandlerWrapper.Reader = reader; #else - reader = XmlUtils.CreateValidatingReader(stream, Resolver, NamespaceParserRegistry.GetSchemas(), HandleValidation); -#endif - } - - #region Instrumentation - - if (log.IsDebugEnabled) - { - log.Debug("Using the following XmlReader implementation : " + reader.GetType()); + reader = XmlUtils.CreateValidatingReader(stream, Resolver, NamespaceParserRegistry.GetSchemas(), HandleValidation); +#endif } - return reader; - + + #region Instrumentation + + if (log.IsDebugEnabled) + { + log.Debug("Using the following XmlReader implementation : " + reader.GetType()); + } + return reader; + #endregion - } - - /// - /// 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) - { - XmlSchemaException ex = args.Exception; - XmlReader xmlReader = (XmlReader) sender; + } + + /// + /// 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) + { + XmlSchemaException ex = args.Exception; + XmlReader xmlReader = (XmlReader)sender; if (!NamespaceParserRegistry.GetSchemas().Contains(xmlReader.NamespaceURI) #if NET_2_0 - && ex is XmlSchemaValidationException + && ex is XmlSchemaValidationException #endif - ) +) { // try wellknown parsers bool registered = NamespaceParserRegistry.RegisterWellknownNamespaceParserType(xmlReader.NamespaceURI); if (registered) - { + { throw new RetryParseException(); } - } + } #if !NET_2_0 // ignore validation errors for well-known 'xml' namespace. This seems to be a bug in net 1.0 + 1.1 if (ex.Message.IndexOf("http://www.w3.org/XML/1998/namespace:") > -1) { return; } -#endif - throw ex; - } - 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 - } -} +#endif + throw ex; + } + 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; + XmlReaderContext readerContext = CreateReaderContext(resource); + readerContext.NamespaceParserResolver = this.NamespaceParserResolver; + documentReader.RegisterObjectDefinitions(doc, readerContext); + return Registry.ObjectDefinitionCount - countBefore; + } + + /// + /// Creates the to use for actually + /// reading object definitions from an XML document. + /// + /// Default implementation instantiates the specified + /// or if no reader type is specified. + /// + protected virtual IObjectDefinitionDocumentReader CreateObjectDefinitionDocumentReader() + { + if (documentReaderType == null) + { + return new DefaultObjectDefinitionDocumentReader(); + } + 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, this.objectDefinitionFactory); + } + + /// + /// Create a instance for handling custom namespaces. + /// + /// + /// TODO (EE): make protected virtual, see remarks on + /// + private INamespaceParserResolver CreateDefaultNamespaceParserResolver() + { + return new DefaultNamespaceHandlerResolver(); + } + + #endregion + } +} diff --git a/src/Spring/Spring.Core/Objects/Factory/Xml/XmlReaderContext.cs b/src/Spring/Spring.Core/Objects/Factory/Xml/XmlReaderContext.cs index 27abf435..32200969 100644 --- a/src/Spring/Spring.Core/Objects/Factory/Xml/XmlReaderContext.cs +++ b/src/Spring/Spring.Core/Objects/Factory/Xml/XmlReaderContext.cs @@ -29,18 +29,14 @@ using Spring.Util; namespace Spring.Objects.Factory.Xml { /// - /// Extension of specific to use with an - /// XmlObjectDefinitionReader. + /// Extension of specific to use with an XmlObjectDefinitionReader. + /// Provides access to configured in /// - /// In future will contain access to IXmlParserRegistry public class XmlReaderContext : ReaderContext { - - //TODO: Should have a ref to NamespaceParserRegistry, i.e. NamespaceHandlerResolver here.... - - private IObjectDefinitionReader reader; - - private IObjectDefinitionFactory objectDefinitionFactory = new DefaultObjectDefinitionFactory(); + private readonly IObjectDefinitionReader reader; + private readonly IObjectDefinitionFactory objectDefinitionFactory; + private INamespaceParserResolver namespaceParserResolver; /// /// The maximum length of any XML fragment displayed in the error message @@ -54,18 +50,32 @@ namespace Spring.Objects.Factory.Xml /// private const int MaxXmlErrorFragmentLength = 255; + /// + /// Initializes a new instance of the class. + /// + /// The resource. + /// The reader. + public XmlReaderContext(IResource resource, IObjectDefinitionReader reader) + : this(resource, reader, new DefaultObjectDefinitionFactory()) + {} + /// /// Initializes a new instance of the class. /// /// The resource. /// The reader. - public XmlReaderContext(IResource resource, IObjectDefinitionReader reader) : base(resource) + /// The factory to use for creating new instances. + internal XmlReaderContext(IResource resource, IObjectDefinitionReader reader, IObjectDefinitionFactory objectDefinitionFactory) + : base(resource) { this.reader = reader; - + if (reader is XmlObjectDefinitionReader) + { + this.namespaceParserResolver = ((XmlObjectDefinitionReader) reader).NamespaceParserResolver; + } + this.objectDefinitionFactory = objectDefinitionFactory; } - /// /// Gets the reader. /// @@ -96,7 +106,6 @@ namespace Spring.Objects.Factory.Xml } } - /// /// Gets or sets the object definition factory. /// @@ -104,10 +113,16 @@ namespace Spring.Objects.Factory.Xml public IObjectDefinitionFactory ObjectDefinitionFactory { get { return objectDefinitionFactory; } - set { objectDefinitionFactory = value; } } - + /// + /// Get the instance to lookup parsers for custom namespaces. + /// + internal INamespaceParserResolver NamespaceParserResolver + { + get { return namespaceParserResolver; } + set { namespaceParserResolver = value; } + } /// /// Generates the name of the object. diff --git a/src/Spring/Spring.Core/Spring.Core.2008.csproj b/src/Spring/Spring.Core/Spring.Core.2008.csproj index 9bd72de8..77657632 100644 --- a/src/Spring/Spring.Core/Spring.Core.2008.csproj +++ b/src/Spring/Spring.Core/Spring.Core.2008.csproj @@ -600,6 +600,8 @@ + + diff --git a/src/Spring/Spring.Core/Util/CollectionUtils.cs b/src/Spring/Spring.Core/Util/CollectionUtils.cs index 3e4ae8db..90c6a0fc 100644 --- a/src/Spring/Spring.Core/Util/CollectionUtils.cs +++ b/src/Spring/Spring.Core/Util/CollectionUtils.cs @@ -72,9 +72,10 @@ namespace Spring.Util /// if the element is in the collection, otherwise. public static bool Contains(ICollection collection, Object element) { + // TODO (EE): does not match Spring/J behavior. Change to IEnumerable and enumerable may be null if (collection == null) { - throw new ArgumentNullException("Collection cannot be null."); + throw new ArgumentNullException("collection", "Collection cannot be null."); } MethodInfo method; method = collection.GetType().GetMethod("contains", BindingFlags.IgnoreCase | BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public); @@ -92,17 +93,32 @@ namespace Spring.Util /// The object to add to the collection. public static void Add(ICollection collection, object element) { - if (collection == null) + Add((IEnumerable)collection, element); + } + + /// + /// Adds the specified to the specified . + /// + /// The enumerable to add the element to. + /// The object to add to the collection. + public static void Add(IEnumerable enumerable, object element) + { + if (enumerable == null) { - throw new ArgumentNullException("Collection cannot be null."); + throw new ArgumentNullException("enumerable", "Collection cannot be null."); + } + if (enumerable is IList) + { + ((IList)enumerable).Add(element); + return; } MethodInfo method; - method = collection.GetType().GetMethod("add", BindingFlags.IgnoreCase | BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public); + method = enumerable.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."); + throw new InvalidOperationException("Enumerable type " + enumerable.GetType() + " does not implement a Add() method."); } - method.Invoke(collection, new Object[] { element }); + method.Invoke(enumerable, new Object[] { element }); } /// @@ -113,9 +129,13 @@ namespace Spring.Util /// 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) + if (targetCollection == null) { - throw new ArgumentNullException("Collection cannot be null."); + throw new ArgumentNullException("targetCollection", "Collection cannot be null."); + } + if (sourceCollection == null) + { + throw new ArgumentNullException("sourceCollection", "Collection cannot be null."); } if (sourceCollection.Count == 0 && targetCollection.Count > 1) return true; @@ -213,6 +233,47 @@ namespace Spring.Util return array; } + /// + /// Returns the first element contained in both, and . + /// + /// The implementation assumes that <<< + /// the source enumerable. may be null + /// the list of candidates to match against elements. may be null + /// the first element found in both enumerables or null + public static object FindFirstMatch(IEnumerable source, IEnumerable candidates) + { + if (IsEmpty(source) || IsEmpty(candidates)) + { + return null; + } + + IList candidateList = candidates as IList; + if (candidateList == null) + { + if (candidates is ICollection) + { + candidateList = new ArrayList((ICollection)candidates); + } + else + { + candidateList = new ArrayList(); + foreach (object el in candidates) + { + candidateList.Add(el); + } + } + } + + foreach (object sourceElement in source) + { + if (candidateList.Contains(sourceElement)) + { + return sourceElement; + } + } + return null; + } + /// /// Finds a value of the given type in the given collection. /// @@ -268,6 +329,31 @@ namespace Spring.Util return null; } + /// + /// Determines whether the specified collection is null or empty. + /// + /// The collection to check. + /// + /// true if the specified collection is empty or null; otherwise, false. + /// + public static bool IsEmpty(IEnumerable enumerable) + { + if (enumerable == null) + return true; + + if (enumerable is ICollection) + { + return (0 == ((ICollection)enumerable).Count); + } + + IEnumerator it = enumerable.GetEnumerator(); + if (!it.MoveNext()) + { + return true; + } + return false; + } + /// /// Determines whether the specified collection is null or empty. /// @@ -337,11 +423,11 @@ namespace Spring.Util ehancedInput.Sort(Entry.GetComparer(comparer)); - for (int i = 0; i < ehancedInput.Count; i++ ) + for (int i = 0; i < ehancedInput.Count; i++) { - ehancedInput[i] = ((Entry) ehancedInput[i]).Value; + ehancedInput[i] = ((Entry)ehancedInput[i]).Value; } - + return ehancedInput; } diff --git a/src/Spring/Spring.Core/Validation/Config/ValidationNamespaceParser.cs b/src/Spring/Spring.Core/Validation/Config/ValidationNamespaceParser.cs index 2b8be123..9632964b 100644 --- a/src/Spring/Spring.Core/Validation/Config/ValidationNamespaceParser.cs +++ b/src/Spring/Spring.Core/Validation/Config/ValidationNamespaceParser.cs @@ -123,7 +123,15 @@ namespace Spring.Validation.Config string parent = GetAttributeValue(element, ObjectDefinitionConstants.ParentAttribute); string name = "validator: " + (StringUtils.HasText(id) ? id : this.definitionCount.ToString()); + MutablePropertyValues properties = new MutablePropertyValues(); + IConfigurableObjectDefinition od + = parserContext.ReaderContext.ObjectDefinitionFactory.CreateObjectDefinition( + typeName, parent, parserContext.ReaderContext.Reader.Domain); + + od.PropertyValues = properties; + od.IsSingleton = true; + od.IsLazyInit = true; ParseAttributeIntoProperty(element, ValidatorDefinitionConstants.TestAttribute, properties, "Test"); ParseAttributeIntoProperty(element, ValidatorDefinitionConstants.WhenAttribute, properties, "When"); @@ -141,6 +149,7 @@ namespace Spring.Validation.Config ManagedList nestedValidators = new ManagedList(); ManagedList actions = new ManagedList(); + ParserContext childParserContext = new ParserContext(parserContext.ParserHelper, od); foreach (XmlNode node in element.ChildNodes) { XmlElement child = node as XmlElement; @@ -150,19 +159,19 @@ namespace Spring.Validation.Config { case ValidatorDefinitionConstants.PropertyElement: string propertyName = GetAttributeValue(child, ValidatorDefinitionConstants.PropertyNameAttribute); - properties.Add(propertyName, base.ParsePropertyValue(child, name, parserContext)); + properties.Add(propertyName, base.ParsePropertyValue(child, name, childParserContext)); break; case ValidatorDefinitionConstants.MessageElement: - actions.Add(ParseErrorMessageAction(child, parserContext)); + actions.Add(ParseErrorMessageAction(child, childParserContext)); break; case ValidatorDefinitionConstants.ActionElement: - actions.Add(ParseGenericAction(child, parserContext)); + actions.Add(ParseGenericAction(child, childParserContext)); break; case ValidatorDefinitionConstants.ReferenceElement: - nestedValidators.Add(ParseValidatorReference(child, parserContext)); + nestedValidators.Add(ParseValidatorReference(child, childParserContext)); break; default: - nestedValidators.Add(ParseAndRegisterValidator(child, parserContext)); + nestedValidators.Add(ParseAndRegisterValidator(child, childParserContext)); break; } } @@ -176,14 +185,6 @@ namespace Spring.Validation.Config properties.Add("Actions", actions); } - IConfigurableObjectDefinition od - = parserContext.ReaderContext.ObjectDefinitionFactory.CreateObjectDefinition( - typeName, parent, parserContext.ReaderContext.Reader.Domain); - - od.PropertyValues = properties; - od.IsSingleton = true; - od.IsLazyInit = true; - return od; } diff --git a/src/Spring/Spring.Web/Context/Support/WebApplicationContext.cs b/src/Spring/Spring.Web/Context/Support/WebApplicationContext.cs index 4ed00327..2d93b0f2 100644 --- a/src/Spring/Spring.Web/Context/Support/WebApplicationContext.cs +++ b/src/Spring/Spring.Web/Context/Support/WebApplicationContext.cs @@ -27,6 +27,7 @@ using System.IO; using System.Reflection; using System.Web; using System.Web.Hosting; +using System.Xml; using Common.Logging; using Spring.Collections; using Spring.Objects; @@ -106,10 +107,7 @@ namespace Spring.Context.Support // remember creation info for debug output this._constructionTimeStamp = DateTime.Now; - if (HttpContext.Current != null) - { - this._constructionUrl = HttpContext.Current.Request.RawUrl; - } + this._constructionUrl = VirtualEnvironment.CurrentVirtualPathAndQuery; if (log.IsDebugEnabled) { log.Debug("created instance " + this.ToString()); @@ -217,7 +215,7 @@ namespace Spring.Context.Support { get { - string requestUrl = HttpContext.Current.Request.FilePath; + string requestUrl = VirtualEnvironment.CurrentVirtualFilePath; return GetContextInternal(requestUrl); } } @@ -351,7 +349,7 @@ namespace Spring.Context.Support /// Reader to initialize. protected override void InitObjectDefinitionReader(XmlObjectDefinitionReader objectDefinitionReader) { - NamespaceParserRegistry.RegisterParser(typeof(WebObjectsNamespaceParser)); +// NamespaceParserRegistry.RegisterParser(typeof(WebObjectsNamespaceParser)); } /// @@ -369,6 +367,16 @@ namespace Spring.Context.Support /// /// Web object factory to use. protected override DefaultListableObjectFactory CreateObjectFactory() + { + string contextPath = GetContextPathWithTrailingSlash(); + return new WebObjectFactory(contextPath, this.CaseSensitive, GetInternalParentObjectFactory()); + } + + /// + /// Returns the application-relative virtual path of this context (without leading '~'!). + /// + /// + private string GetContextPathWithTrailingSlash() { string contextPath = this.Name; if (contextPath == DefaultRootContextName) @@ -379,7 +387,16 @@ namespace Spring.Context.Support { contextPath = contextPath + "/"; } - return new WebObjectFactory(contextPath, this.CaseSensitive, GetInternalParentObjectFactory()); + return contextPath; + } + + /// + /// Create a reader instance capable of handling web objects (Pages,Controls) for importing o + /// bject definitions into the specified . + /// + protected override XmlObjectDefinitionReader CreateXmlObjectDefinitionReader(DefaultListableObjectFactory objectFactory) + { + return new WebObjectDefinitionReader(GetContextPathWithTrailingSlash(), objectFactory, new XmlUrlResolver()); } } } diff --git a/src/Spring/Spring.Web/DataBinding/HttpRequestBindingContainer.cs b/src/Spring/Spring.Web/DataBinding/HttpRequestBindingContainer.cs index f388da04..78189c5d 100644 --- a/src/Spring/Spring.Web/DataBinding/HttpRequestBindingContainer.cs +++ b/src/Spring/Spring.Web/DataBinding/HttpRequestBindingContainer.cs @@ -1,5 +1,6 @@ using System; using System.Collections; +using System.Collections.Specialized; using System.Reflection; using System.Web; using Spring.Core; @@ -97,27 +98,22 @@ namespace Spring.DataBinding /// 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; + NameValueCollection parameters = VirtualEnvironment.RequestParams; 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) + if (parameters.GetValues(requestParams[0]) != null) { - int valueCount = request.Params.GetValues(requestParams[0]).Length; + int valueCount = parameters.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]; + vars[paramName] = parameters.GetValues(paramName)[i]; } object targetItem; diff --git a/src/Spring/Spring.Web/Objects/Factory/Support/ChildWebObjectDefinition.cs b/src/Spring/Spring.Web/Objects/Factory/Support/ChildWebObjectDefinition.cs index 0579a2bf..36fdd438 100644 --- a/src/Spring/Spring.Web/Objects/Factory/Support/ChildWebObjectDefinition.cs +++ b/src/Spring/Spring.Web/Objects/Factory/Support/ChildWebObjectDefinition.cs @@ -58,7 +58,8 @@ namespace Spring.Objects.Factory.Support /// 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) + public ChildWebObjectDefinition(string parentName, Type type, ConstructorArgumentValues arguments, MutablePropertyValues properties) + : base(parentName, type, arguments, properties) {} /// @@ -76,7 +77,8 @@ namespace Spring.Objects.Factory.Support /// 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) + public ChildWebObjectDefinition(string parentName, string typeName, ConstructorArgumentValues arguments, MutablePropertyValues properties) + : base(parentName, typeName, arguments, properties) {} /// @@ -93,7 +95,7 @@ namespace Spring.Objects.Factory.Support public ChildWebObjectDefinition(string parentName, string pageName, MutablePropertyValues properties) : base(parentName, WebObjectUtils.GetPageType(pageName), null, properties) { - _pageName = WebUtils.CombineVirtualPaths(HttpContext.Current.Request.CurrentExecutionFilePath, pageName); + _pageName = WebUtils.CombineVirtualPaths(VirtualEnvironment.CurrentExecutionFilePath, pageName); } #endregion diff --git a/src/Spring/Spring.Web/Objects/Factory/Support/IWebObjectNameGenerator.cs b/src/Spring/Spring.Web/Objects/Factory/Support/IWebObjectNameGenerator.cs new file mode 100644 index 00000000..4b403f7f --- /dev/null +++ b/src/Spring/Spring.Web/Objects/Factory/Support/IWebObjectNameGenerator.cs @@ -0,0 +1,33 @@ +#region License + +/* + * Copyright 2002-2009 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS 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.Support +{ + /// + /// + /// Erich Eichinger + internal interface IWebObjectNameGenerator + { + string CreatePageDefinitionName(string virtualPath); + string CreateControlDefinitionName(string virtualPath); + } +} \ 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 index 46b328b3..c803911c 100644 --- a/src/Spring/Spring.Web/Objects/Factory/Support/RootWebObjectDefinition.cs +++ b/src/Spring/Spring.Web/Objects/Factory/Support/RootWebObjectDefinition.cs @@ -116,7 +116,7 @@ namespace Spring.Objects.Factory.Support MutablePropertyValues properties) : base(WebObjectUtils.GetPageType(pageName), null, properties) { - _pageName = WebUtils.CombineVirtualPaths(HttpContext.Current.Request.CurrentExecutionFilePath, pageName); + _pageName = WebUtils.CombineVirtualPaths(VirtualEnvironment.CurrentExecutionFilePath, pageName); } /// diff --git a/src/Spring/Spring.Web/Objects/Factory/Support/WebObjectFactory.cs b/src/Spring/Spring.Web/Objects/Factory/Support/WebObjectFactory.cs index 3e8a49b1..7a1c1d26 100644 --- a/src/Spring/Spring.Web/Objects/Factory/Support/WebObjectFactory.cs +++ b/src/Spring/Spring.Web/Objects/Factory/Support/WebObjectFactory.cs @@ -127,6 +127,14 @@ namespace Spring.Objects.Factory.Support #endregion + /// + /// Returns the virtual path this object factory is associated with. + /// + public string ContextPath + { + get { return contextPath; } + } + #region Convenience accessors for Http* objects /// diff --git a/src/Spring/Spring.Web/Objects/Factory/Support/WebObjectUtils.cs b/src/Spring/Spring.Web/Objects/Factory/Support/WebObjectUtils.cs index a14eb542..fa88a70b 100644 --- a/src/Spring/Spring.Web/Objects/Factory/Support/WebObjectUtils.cs +++ b/src/Spring/Spring.Web/Objects/Factory/Support/WebObjectUtils.cs @@ -27,9 +27,11 @@ using System.Web.UI; #endif using System; using System.IO; -using System.Web; +//using System.Web; using Common.Logging; using Spring.Util; +using IHttpHandler = System.Web.IHttpHandler; +using HttpException = System.Web.HttpException; namespace Spring.Objects.Factory.Support { @@ -91,16 +93,16 @@ namespace Spring.Objects.Factory.Support s_log.Debug( "creating page instance '" + pageUrl + "'" ); } - HttpContext ctx = HttpContext.Current; - if (ctx == null) - { - throw new ObjectCreationException( - "Unable to instantiate page. HttpContext is not defined." ); - } - IHttpHandler page = null; +// HttpContext ctx = HttpContext.Current; +// if (ctx == null) +// { +// throw new ObjectCreationException( +// "Unable to instantiate page. HttpContext is not defined." ); +// } + IHttpHandler page; try { - page = CreateHandler( ctx, pageUrl ); + page = CreateHandler( pageUrl ); } catch (HttpException httpEx) { @@ -132,25 +134,26 @@ namespace Spring.Objects.Factory.Support /// /// Creates the raw handler instance without any exception handling /// - /// /// /// - internal static IHttpHandler CreateHandler( HttpContext ctx, string pageUrl ) + internal static IHttpHandler CreateHandler( string pageUrl ) { IHttpHandler page; -#if NET_1_1 - string physicalPath = ctx.Server.MapPath(pageUrl); - s_log.Debug(string.Format("constructing page virtual path '{0}' from physical file '{1}'", pageUrl, physicalPath)); - page = PageParser.GetCompiledPageInstance(pageUrl, physicalPath, ctx); -#else - 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 +// HttpContext ctx = HttpContext.Current; +//#if NET_1_1 +// string physicalPath = ctx.Server.MapPath(pageUrl); +// s_log.Debug(string.Format("constructing page virtual path '{0}' from physical file '{1}'", pageUrl, physicalPath)); +// page = PageParser.GetCompiledPageInstance(pageUrl, physicalPath, ctx); +//#else +// 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 + page = VirtualEnvironment.CreateInstanceFromVirtualPath(pageUrl, typeof (IHttpHandler)) as IHttpHandler; return page; } @@ -188,11 +191,11 @@ namespace Spring.Objects.Factory.Support { AssertUtils.ArgumentHasText( pageUrl, "pageUrl" ); - HttpContext ctx = HttpContext.Current; - if (ctx == null) - { - throw new ObjectCreationException( "Unable to get page type. HttpContext is not defined." ); - } +// HttpContext ctx = HttpContext.Current; +// if (ctx == null) +// { +// throw new ObjectCreationException( "Unable to get page type. HttpContext is not defined." ); +// } try { @@ -209,10 +212,10 @@ namespace Spring.Objects.Factory.Support /// /// Calls the underlying ASP.NET infrastructure to obtain the compiled page type - /// relative to the current . + /// relative to the current . /// /// - /// The filename of the ASPX page relative to the current + /// The filename of the ASPX page relative to the current /// /// /// The of the ASPX page @@ -225,18 +228,18 @@ namespace Spring.Objects.Factory.Support s_log.Debug( "getting page type for " + pageUrl ); } - string rootedVPath = WebUtils.CombineVirtualPaths( HttpContext.Current.Request.CurrentExecutionFilePath, pageUrl ); + string rootedVPath = WebUtils.CombineVirtualPaths( VirtualEnvironment.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 + Type pageType = VirtualEnvironment.GetCompiledType(rootedVPath); +//#if NET_2_0 +// pageType = BuildManager.GetCompiledType( rootedVPath ); // requires rooted virtual path! +//#else +// pageType = CreatePageInstance(pageUrl).GetType(); +//#endif if (s_log.IsDebugEnabled) { @@ -257,27 +260,28 @@ namespace Spring.Objects.Factory.Support 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." ); - } +// 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 ); + string rootedVPath = WebUtils.CombineVirtualPaths( VirtualEnvironment.CurrentExecutionFilePath, controlName ); if (s_log.IsDebugEnabled) { s_log.Debug( "control vpath is " + rootedVPath ); } - Type controlType = null; + Type controlType; 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 +//#if NET_2_0 +// controlType = BuildManager.GetCompiledType( rootedVPath ); // requires rooted virtual path! +//#else +// controlType = (Type) miGetCompiledUserControlType.Invoke(null, new object[] { rootedVPath, null, ctx }); +//#endif + controlType = VirtualEnvironment.GetCompiledType(rootedVPath); } catch (HttpException httpEx) { diff --git a/src/Spring/Spring.Web/Objects/Factory/Xml/WebObjectDefinitionDocumentReader.cs b/src/Spring/Spring.Web/Objects/Factory/Xml/WebObjectDefinitionDocumentReader.cs new file mode 100644 index 00000000..0fe48384 --- /dev/null +++ b/src/Spring/Spring.Web/Objects/Factory/Xml/WebObjectDefinitionDocumentReader.cs @@ -0,0 +1,49 @@ +#region License + +/* + * Copyright 2002-2009 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR 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.Support; +using Spring.Util; + +namespace Spring.Objects.Factory.Xml +{ + /// + /// An capable of handling web objects (Pages,Controls). + /// + /// Erich Eichinger + internal class WebObjectDefinitionDocumentReader : DefaultObjectDefinitionDocumentReader + { + private readonly IWebObjectNameGenerator webObjectNameGenerator; + + public WebObjectDefinitionDocumentReader(IWebObjectNameGenerator webObjectNameGenerator) + { + AssertUtils.ArgumentNotNull(webObjectNameGenerator, "webObjectNameGenerator"); + this.webObjectNameGenerator = webObjectNameGenerator; + } + + /// + /// Creates an instance for the given + /// and element. + /// + protected override ObjectDefinitionParserHelper CreateHelper(XmlReaderContext readerContext, System.Xml.XmlElement root) + { + return new WebObjectDefinitionParserHelper(webObjectNameGenerator, readerContext, root); + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Web/Objects/Factory/Xml/WebObjectDefinitionParserHelper.cs b/src/Spring/Spring.Web/Objects/Factory/Xml/WebObjectDefinitionParserHelper.cs new file mode 100644 index 00000000..bcd906fb --- /dev/null +++ b/src/Spring/Spring.Web/Objects/Factory/Xml/WebObjectDefinitionParserHelper.cs @@ -0,0 +1,149 @@ +#region License + +/* + * Copyright 2002-2009 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS 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.Config; +using Spring.Objects.Factory.Support; +using Spring.Util; + +namespace Spring.Objects.Factory.Xml +{ + /// + /// An capable of handling web objects (Pages,Controls) + /// + /// Erich Eichinger + internal class WebObjectDefinitionParserHelper : ObjectDefinitionParserHelper + { + private readonly IWebObjectNameGenerator webObjectNameGenerator; + + /// + /// Initializes a new instance of the class. + /// + /// used for generating object definition names from web object types (page, control) + /// The reader context. + /// The root element of the xml document to parse + public WebObjectDefinitionParserHelper(IWebObjectNameGenerator webObjectNameGenerator, XmlReaderContext readerContext, XmlElement root) + : base(readerContext, root) + { + AssertUtils.ArgumentNotNull(webObjectNameGenerator, "webObjectNameGenerator"); + this.webObjectNameGenerator = webObjectNameGenerator; + } + + protected override string PostProcessObjectNameAndAliases(string objectName, System.Collections.ArrayList aliases, XmlElement element, IObjectDefinition containingDefinition) + { + string url = element.GetAttribute(ObjectDefinitionConstants.TypeAttribute); + string strTypeName = url.ToLower(); + if (strTypeName.EndsWith(".aspx")) + { + if (!StringUtils.HasText(objectName)) + { + objectName = webObjectNameGenerator.CreatePageDefinitionName(url); + } + + // adjust aliases if necessary + for (int ai = 0; ai < aliases.Count; ai++) + { + string alias = (string)aliases[ai]; + if (alias != null && alias.Length > 0 && alias[0] == '~') + { + aliases[ai] = "/" + alias.Substring(1).TrimStart('/', '\\'); + } + } + } + else if (strTypeName.EndsWith(".ascx") || strTypeName.EndsWith(".master")) + { + if (!StringUtils.HasText(objectName)) + { + objectName = webObjectNameGenerator.CreateControlDefinitionName(url); + } + } + + return objectName; + } + + protected override ObjectDefinitionHolder CreateObjectDefinitionHolder(XmlElement element, IConfigurableObjectDefinition definition, string objectName, string[] aliasesArray) + { + 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; + } + } + + ObjectDefinitionHolder holder = base.CreateObjectDefinitionHolder(element, definition, objectName, aliasesArray); + return holder; + } + + /// + /// 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/Objects/Factory/Xml/WebObjectDefinitionReader.cs b/src/Spring/Spring.Web/Objects/Factory/Xml/WebObjectDefinitionReader.cs new file mode 100644 index 00000000..f6402f45 --- /dev/null +++ b/src/Spring/Spring.Web/Objects/Factory/Xml/WebObjectDefinitionReader.cs @@ -0,0 +1,108 @@ +#region License + +/* + * Copyright 2002-2009 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS 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; +using System.Xml; +using Spring.Objects.Factory.Support; +using Spring.Util; + +namespace Spring.Objects.Factory.Xml +{ + /// + /// An capable of handling web object definitions (Pages, Controls) + /// + /// Erich Eichinger + public class WebObjectDefinitionReader : XmlObjectDefinitionReader, IWebObjectNameGenerator + { + private readonly string contextVirtualPath; + + /// + /// Creates a new instance of the + /// class. + /// + /// the (rooted) virtual path to resolve relative virtual paths. + /// + /// The + /// instance that this reader works on. + /// + /// the to use for resolving entities. + public WebObjectDefinitionReader(string contextVirtualPath, IObjectDefinitionRegistry registry, XmlResolver resolver) + : base(registry, resolver, new WebObjectDefinitionFactory()) + { + this.contextVirtualPath = contextVirtualPath; + } + + /// + /// Creates the to use for actually + /// reading object definitions from an XML document. + /// + protected override IObjectDefinitionDocumentReader CreateObjectDefinitionDocumentReader() + { + return new WebObjectDefinitionDocumentReader(this); + } + + string IWebObjectNameGenerator.CreatePageDefinitionName(string virtualPath) + { + return CreatePageDefinitionName(virtualPath); + } + + string IWebObjectNameGenerator.CreateControlDefinitionName(string virtualPath) + { + return CreateControlDefinitionName(virtualPath); + } + + /// + /// Create an object definition name for the given control path + /// + protected virtual string CreateControlDefinitionName(string virtualPath) + { + string objectName; + objectName = WebObjectUtils.GetControlType(virtualPath).FullName; + return objectName; + } + + /// + /// Create an object definition name for the given page path + /// + protected virtual string CreatePageDefinitionName(string url) + { + string objectName; + objectName = WebUtils.CombineVirtualPaths(VirtualEnvironment.CurrentExecutionFilePath, url); + string appPath = VirtualEnvironment.ApplicationVirtualPath; + if (objectName.ToLower().StartsWith(appPath.ToLower())) + { + objectName = objectName.Substring(appPath.Length-1); + } + +// System.Web.UI.Page page = (System.Web.UI.Page)WebObjectUtils.CreatePageInstance(url); +//#if NET_2_0 +// objectName = page.AppRelativeVirtualPath.Substring(1); +//#else +// string appPath = HttpContext.Current.Request.ApplicationPath.TrimEnd('\\', '/'); +// objectName = page.TemplateSourceDirectory.TrimEnd('\\','/') + "/" + WebUtils.GetPageName(url) + ".aspx"; +// if (objectName.ToLower().StartsWith(appPath.ToLower())) +// { +// objectName = objectName.Substring(appPath.Length); +// } +//#endif + return objectName; + } + } +} \ 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 index a5c5edf3..c35f0534 100644 --- a/src/Spring/Spring.Web/Objects/Factory/Xml/WebObjectsNamespaceParser.cs +++ b/src/Spring/Spring.Web/Objects/Factory/Xml/WebObjectsNamespaceParser.cs @@ -48,7 +48,7 @@ namespace Spring.Objects.Factory.Xml /// public class WebObjectsNamespaceParser : ObjectsNamespaceParser { - private IObjectDefinitionFactory objectDefinitionFactory; +// private IObjectDefinitionFactory objectDefinitionFactory; #region Constructor (s) / Destructor @@ -58,148 +58,148 @@ namespace Spring.Objects.Factory.Xml ///
public WebObjectsNamespaceParser() { - objectDefinitionFactory = new WebObjectDefinitionFactory(); +// 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 ParseObjectDefinitionElement( - XmlElement element, string id, ParserContext parserContext) - { - parserContext.ReaderContext.ObjectDefinitionFactory = objectDefinitionFactory; - IConfigurableObjectDefinition definition = base.ParseObjectDefinitionElement(element, id, parserContext); - IWebObjectDefinition webDefinition = definition as IWebObjectDefinition; - - if (webDefinition != null) - { - webDefinition.Scope = GetScope(element.GetAttribute(ObjectDefinitionConstants.ScopeAttribute)); +// /// +// /// 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 ParseObjectDefinitionElement( +// XmlElement element, string id, ParserContext parserContext) +// { +// IConfigurableObjectDefinition definition = base.ParseObjectDefinitionElement(element, id, parserContext); +// 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; +// } - // 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) +// { +// return null; +// 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;ai0 && alias[0]=='~') +// { +// aliases[ai] = "/"+alias.Substring(1).TrimStart('/','\\'); +// } +// } +// } +// else if (strTypeName.EndsWith(".ascx") || strTypeName.EndsWith(".master")) +// { +// id = WebObjectUtils.GetControlType(strTypeName).FullName; +// } +// else +// { +// id = base.CalculateId(element, aliases); +// } +// return id; +// } - /// - /// 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; - } +// /// +// /// 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.2008.csproj b/src/Spring/Spring.Web/Spring.Web.2008.csproj index 4b0b17b7..eeaf39df 100644 --- a/src/Spring/Spring.Web/Spring.Web.2008.csproj +++ b/src/Spring/Spring.Web/Spring.Web.2008.csproj @@ -125,6 +125,10 @@ Code + + + + diff --git a/src/Spring/Spring.Web/Util/HttpContextSwitch.cs b/src/Spring/Spring.Web/Util/HttpContextSwitch.cs index 3cf8b0d4..692d1e21 100644 --- a/src/Spring/Spring.Web/Util/HttpContextSwitch.cs +++ b/src/Spring/Spring.Web/Util/HttpContextSwitch.cs @@ -49,34 +49,60 @@ namespace Spring.Util /// Erich Eichinger public class HttpContextSwitch : IDisposable { - private HttpContext savedContext; - private string originalUrl; + private readonly IDisposable rewriteContext; 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); +// } +// } + /// /// Performs an immediate call to /// /// a directory path (without trailing filename!) - public HttpContextSwitch( string virtualDirectory ) + public HttpContextSwitch(string virtualDirectory) { - HttpContext currentContext = HttpContext.Current; - if (currentContext == null) return; // no webrequest + rewriteContext = VirtualEnvironment.RewritePath(virtualDirectory, false); - 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); - } +// string currentFileDirectory = WebUtils.GetVirtualDirectory(VirtualEnvironment.CurrentVirtualFilePath); +// // only switch path if necessary +// if (string.Compare(virtualDirectory, currentFileDirectory, true) != 0) +// { +// originalUrl = VirtualEnvironment.CurrentVirtualPathAndQuery; +// string newPath = virtualDirectory + "currentcontext.dummy"; +// VirtualEnvironment.RewritePath(newPath, false); +// +// #region Instrumentation +// +// if (log.IsDebugEnabled) +// { +// log.Debug("rewriting path from " + originalUrl + " to " + newPath + " results in " + VirtualEnvironment.CurrentVirtualFilePath); +// } +// +// #endregion +// } } /// @@ -84,17 +110,45 @@ namespace Spring.Util /// 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 - } + rewriteContext.Dispose(); +// if (rewriteContext != null) +// { +// VirtualEnvironment.RewritePath(originalUrl, false); +// +// #region Instrumentation +// +// if (log.IsDebugEnabled) +// { +// log.Debug("restoring original path from " + VirtualEnvironment.CurrentVirtualFilePath + " back to " + originalUrl); +// } +// +// #endregion +// } } + +// /// +// /// Restores original path if necessary +// /// +// public void Dispose() +// { +// if (originalUrl != null) +// { +// HttpContext context = HttpContext.Current; +//#if NET_2_0 +// context.RewritePath( originalUrl, false ); +//#else +// context.RewritePath( originalUrl ); +//#endif +// +// #region Instrumentation +// +// if (log.IsDebugEnabled) +// { +// log.Debug("restoring original path from " + context.Request.FilePath + " back to " + originalUrl); +// } +// +// #endregion +// } +// } } } \ No newline at end of file diff --git a/src/Spring/Spring.Web/Util/IVirtualEnvironment.cs b/src/Spring/Spring.Web/Util/IVirtualEnvironment.cs index cafa3aa3..bf004541 100644 --- a/src/Spring/Spring.Web/Util/IVirtualEnvironment.cs +++ b/src/Spring/Spring.Web/Util/IVirtualEnvironment.cs @@ -22,6 +22,7 @@ using System; using System.Collections; +using System.Collections.Specialized; using System.Globalization; using System.Reflection; using System.Web; @@ -64,17 +65,40 @@ namespace Spring.Util /// string CurrentExecutionFilePath { get; } /// + /// The query parameters + /// + NameValueCollection QueryString { get; } + /// /// Maps a virtual path to it's physical location /// string MapPath( string virtualPath ); /// + /// Rewrites the , thus also affecting + /// + IDisposable RewritePath(string newVirtualPath, bool rebaseClientPath); + /// /// Returns the current Session's variable dictionary /// ISessionState Session { get; } /// - /// Returns the current Request's variable dictionary + /// Returns the current Request's variable dictionary /// IDictionary RequestVariables { get; } - + /// + /// Returns the current Request's parameter dictionary + /// + NameValueCollection RequestParams { get; } + /// + /// Get the compiled type for the given virtual path + /// + /// the absolute (=rooted) virtual path + /// + Type GetCompiledType(string absoluteVirtualPath); + /// + /// Creates an instance from the given virtual path + /// + /// the absolute (=rooted) virtual path + /// the required base type + object CreateInstanceFromVirtualPath(string absoluteVirtualPath, Type requiredBaseType); } } \ No newline at end of file diff --git a/src/Spring/Spring.Web/Util/VirtualEnvironment.cs b/src/Spring/Spring.Web/Util/VirtualEnvironment.cs index 575426e8..29826021 100644 --- a/src/Spring/Spring.Web/Util/VirtualEnvironment.cs +++ b/src/Spring/Spring.Web/Util/VirtualEnvironment.cs @@ -22,10 +22,15 @@ using System; using System.Collections; +using System.Collections.Specialized; using System.IO; +using System.Reflection; using System.Web; using System.Web.Caching; +using System.Web.Compilation; using System.Web.SessionState; +using System.Web.UI; +using Common.Logging; #endregion @@ -229,6 +234,75 @@ namespace Spring.Util #endregion //HttpSessionState Adapter + private static readonly ILog log = LogManager.GetLogger(typeof (HttpRuntimeEnvironment)); + +#if NET_1_1 + // Required method for resolving control types + private static MethodInfo miGetCompiledUserControlType = null; + + static HttpRuntimeEnvironment() + { + Type tUserControlParser = typeof(System.Web.UI.UserControl).Assembly.GetType("System.Web.UI.UserControlParser"); + miGetCompiledUserControlType = tUserControlParser.GetMethod("GetCompiledUserControlType", BindingFlags.Static | BindingFlags.NonPublic); + } +#endif + private class RewriteContext : IDisposable + { + private string originalPath; + private bool rebaseClientPath; + private HttpContext ctx; + + public RewriteContext(string virtualDirectory, bool rebaseClientPath) + { + ctx = HttpContext.Current; + if (ctx == null) + { + return; + } + + this.rebaseClientPath = rebaseClientPath; + + string newVirtualPath = WebUtils.GetVirtualDirectory(virtualDirectory); + string currentFileDirectory = WebUtils.GetVirtualDirectory(ctx.Request.FilePath); + // only switch path if necessary + if (string.Compare(newVirtualPath, currentFileDirectory, true) != 0) + { + originalPath = ctx.Request.Url.PathAndQuery; + string newPath = newVirtualPath + "currentcontext.dummy"; +#if NET_1_1 + ctx.RewritePath(newPath); +#else + ctx.RewritePath(newPath, rebaseClientPath); +#endif + + #region Instrumentation + + if (log.IsDebugEnabled) + { + log.Debug("rewriting path from " + currentFileDirectory + " to " + newPath + " results in " + ctx.Request.FilePath); + } + + #endregion + } + } + + public void Dispose() + { + if (originalPath != null) + { + if (log.IsDebugEnabled) + { + log.Debug("restoring path from " + ctx.Request.FilePath + " back to " + originalPath); + } +#if NET_1_1 + ctx.RewritePath(originalPath); +#else + ctx.RewritePath(originalPath, rebaseClientPath); +#endif + } + } + } + public string ApplicationVirtualPath { get @@ -254,6 +328,11 @@ namespace Spring.Util get { return HttpContext.Current.Request.CurrentExecutionFilePath; } } + public NameValueCollection QueryString + { + get { return HttpContext.Current.Request.QueryString; } + } + public string MapPath(string virtualPath) { HttpContext ctx = HttpContext.Current; @@ -261,8 +340,9 @@ namespace Spring.Util { return ctx.Request.MapPath(virtualPath); } -#if NET_2_0 - +#if NET_1_1 + throw new ArgumentException("can't map context relative path outside a context"); +#else if (VirtualPathUtility.IsAbsolute(virtualPath) && virtualPath.StartsWith(HttpRuntime.AppDomainAppVirtualPath)) { virtualPath = VirtualPathUtility.ToAppRelative(virtualPath); @@ -273,8 +353,13 @@ namespace Spring.Util string physicalPath = Path.Combine(HttpRuntime.AppDomainAppPath, virtualPath); return physicalPath; } + return virtualPath; #endif - throw new ArgumentException("can't map context relative path outside a context"); + } + + public IDisposable RewritePath(string virtualDirectory, bool rebaseClientPath) + { + return new RewriteContext(virtualDirectory, rebaseClientPath); } public ISessionState Session @@ -286,6 +371,49 @@ namespace Spring.Util { get { return HttpContext.Current.Items; } } + + public NameValueCollection RequestParams + { + get { return HttpContext.Current.Request.Params; } + } + + public Type GetCompiledType(string virtualPath) + { + string rootedVPath = WebUtils.CombineVirtualPaths(CurrentExecutionFilePath, virtualPath); + + Type type = null; +#if NET_1_1 + if (virtualPath.EndsWith(".aspx")) + { + type = CreateInstanceFromVirtualPath(virtualPath, typeof(Page)).GetType(); + } + else if (virtualPath.EndsWith(".ascx")) + { + type = (Type)miGetCompiledUserControlType.Invoke(null, new object[] { rootedVPath, null, HttpContext.Current }); + } +#else + type = BuildManager.GetCompiledType(rootedVPath); // requires rooted virtual path! +#endif + return type; + } + + public object CreateInstanceFromVirtualPath(string virtualPath, Type requiredBaseType) + { + string rootedVPath = WebUtils.CombineVirtualPaths(CurrentExecutionFilePath, virtualPath); + object result; +#if NET_1_1 + HttpContext ctx = HttpContext.Current; + string physicalPath = ctx.Server.MapPath(rootedVPath); + result = PageParser.GetCompiledPageInstance(virtualPath, physicalPath, ctx); +#else + result = BuildManager.CreateInstanceFromVirtualPath(rootedVPath, requiredBaseType); +#endif + if (!requiredBaseType.IsAssignableFrom(result.GetType())) + { + throw new HttpException(string.Format("Type '{0}' from virtual path '{1}' does not inherit from '{2}'", result.GetType(), rootedVPath, requiredBaseType)); + } + return result; + } } #endregion @@ -306,6 +434,22 @@ namespace Spring.Util get { return instance.CurrentVirtualPath; } } + /// + /// The virtual (rooted) path of the current Request including + /// + public static string CurrentVirtualPathAndQuery + { + get + { + string result = CurrentVirtualPath; + if (QueryString.Count > 0) + { + result = result + "?" + QueryString.ToString(); + } + return result; + } + } + /// /// The virtual (rooted) path of the current Request without trailing /// @@ -322,6 +466,30 @@ namespace Spring.Util get { return instance.CurrentExecutionFilePath; } } + /// + /// The query parameters + /// + public static NameValueCollection QueryString + { + get { return instance.QueryString; } + } + + /// + /// Returns the current Request's variable dictionary () + /// + public static IDictionary RequestVariables + { + get { return instance.RequestVariables; } + } + + /// + /// Returns the current Request's parameter dictionary () + /// + public static NameValueCollection RequestParams + { + get { return instance.RequestParams; } + } + /// /// Maps a virtual path to it's physical location /// @@ -330,6 +498,32 @@ namespace Spring.Util return instance.MapPath(virtualPath); } + /// + /// Rewrites the , thus also affecting + /// + public static IDisposable RewritePath(string newVirtualPath, bool rebaseClientPath) + { + return instance.RewritePath(newVirtualPath, rebaseClientPath); + } + + /// + /// Returns an instance of the specified file. + /// + public static object CreateInstanceFromVirtualPath(string virtualPath, Type requiredBaseType) + { + string rootedVPath = WebUtils.CombineVirtualPaths(instance.CurrentExecutionFilePath, virtualPath); + return instance.CreateInstanceFromVirtualPath(rootedVPath, requiredBaseType); + } + + /// + /// Returns an the compiled type of the specified file. + /// + public static Type GetCompiledType(string virtualPath) + { + string rootedVPath = WebUtils.CombineVirtualPaths(instance.CurrentExecutionFilePath, virtualPath); + return instance.GetCompiledType(rootedVPath); + } + /// /// Receives EndRequest-event from an instance /// and dispatches it to all handlers registered with this module. diff --git a/src/Spring/Spring.Web/Web/Support/ControlAccessor.cs b/src/Spring/Spring.Web/Web/Support/ControlAccessor.cs index 8a8e0f78..cbd4b2f5 100644 --- a/src/Spring/Spring.Web/Web/Support/ControlAccessor.cs +++ b/src/Spring/Spring.Web/Web/Support/ControlAccessor.cs @@ -318,7 +318,7 @@ namespace Spring.Web.Support return handler; } #else - private static readonly IDynamicField fControls = SafeField.CreateFrom(GetField("_controls")); + private static readonly IDynamicField fControls = new SafeField(GetField("_controls")); private ControlCollection GetChildControlCollection() { diff --git a/src/Spring/Spring.Web/Web/Support/PageHandlerFactory.cs b/src/Spring/Spring.Web/Web/Support/PageHandlerFactory.cs index 58146626..3207f32a 100644 --- a/src/Spring/Spring.Web/Web/Support/PageHandlerFactory.cs +++ b/src/Spring/Spring.Web/Web/Support/PageHandlerFactory.cs @@ -111,7 +111,7 @@ namespace Spring.Web.Support } else { - handler = WebObjectUtils.CreateHandler(context, rawUrl); + handler = WebObjectUtils.CreateHandler(rawUrl); // let WebSupportModule handle configuration handler = WebSupportModule.ConfigureHandler(context, handler, appContext, rawUrl, false); } diff --git a/test/Spring/Spring.Core.Tests/Objects/Factory/Xml/XmlObjectFactoryTests.cs b/test/Spring/Spring.Core.Tests/Objects/Factory/Xml/XmlObjectFactoryTests.cs index 9a8ead40..86fd95bd 100644 --- a/test/Spring/Spring.Core.Tests/Objects/Factory/Xml/XmlObjectFactoryTests.cs +++ b/test/Spring/Spring.Core.Tests/Objects/Factory/Xml/XmlObjectFactoryTests.cs @@ -1784,7 +1784,8 @@ namespace Spring.Objects.Factory.Xml "; stream = new MemoryStream(Encoding.UTF8.GetBytes(xml)); IObjectFactory factory = new XmlObjectFactory(new InputStreamResource(stream, string.Empty)); - factory.GetObject("foo"); + ITestObject to = (ITestObject) factory.GetObject("foo"); + Assert.IsNotNull( to.Spouse ); } [Test(Description="SPR-1313")] diff --git a/test/Spring/Spring.Core.Tests/Spring.Core.Tests.2008.csproj b/test/Spring/Spring.Core.Tests/Spring.Core.Tests.2008.csproj index a0ce0057..0cd3561c 100644 --- a/test/Spring/Spring.Core.Tests/Spring.Core.Tests.2008.csproj +++ b/test/Spring/Spring.Core.Tests/Spring.Core.Tests.2008.csproj @@ -803,6 +803,7 @@ + diff --git a/test/Spring/Spring.Core.Tests/Util/CollectionUtilsTests.cs b/test/Spring/Spring.Core.Tests/Util/CollectionUtilsTests.cs index 28062de8..fdae54ff 100644 --- a/test/Spring/Spring.Core.Tests/Util/CollectionUtilsTests.cs +++ b/test/Spring/Spring.Core.Tests/Util/CollectionUtilsTests.cs @@ -404,5 +404,23 @@ namespace Spring.Util return ((int)dex.Key).CompareTo(dey.Key); } + + [Test] + public void FindFirstMatchReturnsNullIfAnyInputIsEmpty() + { + Assert.IsNull( CollectionUtils.FindFirstMatch(null, null) ); + Assert.IsNull( CollectionUtils.FindFirstMatch(new string[0], new string[0])); + Assert.IsNull( CollectionUtils.FindFirstMatch(null, new string[] { "x" })); + Assert.IsNull( CollectionUtils.FindFirstMatch(new string[] { "x" }, null)); + } + + [Test] + public void FindFirstMatchReturnsFirstMatch() + { + ArrayList source = new ArrayList(); + string[] candidates = new string[] { "G", "B", "H" }; + source.AddRange( new string[] { "A", "B", "C" } ); + Assert.AreEqual( "B" , CollectionUtils.FindFirstMatch(source, candidates)); + } } } diff --git a/test/Spring/Spring.Core.Tests/Validation/HelperClasses.cs b/test/Spring/Spring.Core.Tests/Validation/HelperClasses.cs index 06ae2b2b..ba7a62a5 100644 --- a/test/Spring/Spring.Core.Tests/Validation/HelperClasses.cs +++ b/test/Spring/Spring.Core.Tests/Validation/HelperClasses.cs @@ -126,6 +126,11 @@ namespace Spring.Validation { throw new NotImplementedException(); } + + public bool IsObjectNameInUse(string objectName) + { + return this.objects[objectName] != 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 index 3b9da676..56060416 100644 --- a/test/Spring/Spring.Web.Tests/Core/IO/WebResourceTests.cs +++ b/test/Spring/Spring.Web.Tests/Core/IO/WebResourceTests.cs @@ -39,7 +39,7 @@ namespace Spring.Core.IO [TestFixtureSetUp] public void SetUpFixture() { - testVirtualEnvironment = new VirtualEnvironmentMock("/some.request", "somepathinfo", "/", true); + testVirtualEnvironment = new VirtualEnvironmentMock("/some.request", "somepathinfo", null, "/", true); } [TestFixtureTearDown] diff --git a/test/Spring/Spring.Web.Tests/Objects/Factory/Support/WebObjectFactoryTests.cs b/test/Spring/Spring.Web.Tests/Objects/Factory/Support/WebObjectFactoryTests.cs index dcd22910..7910c467 100644 --- a/test/Spring/Spring.Web.Tests/Objects/Factory/Support/WebObjectFactoryTests.cs +++ b/test/Spring/Spring.Web.Tests/Objects/Factory/Support/WebObjectFactoryTests.cs @@ -44,7 +44,7 @@ namespace Spring.Objects.Factory.Support // 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)) + using (new VirtualEnvironmentMock("/somedir/some.file", null, null, "/", true)) { wof = new WebObjectFactory("/somedir/", false); } diff --git a/test/Spring/Spring.Web.Tests/Objects/Factory/Support/WebObjectUtilsTests.cs b/test/Spring/Spring.Web.Tests/Objects/Factory/Support/WebObjectUtilsTests.cs new file mode 100644 index 00000000..c58d7214 --- /dev/null +++ b/test/Spring/Spring.Web.Tests/Objects/Factory/Support/WebObjectUtilsTests.cs @@ -0,0 +1,60 @@ +#region License + +/* + * Copyright 2002-2009 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS 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.Factory.Support +{ + /// + /// + /// Erich Eichinger + [TestFixture] + public class WebObjectUtilsTests + { + [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"); + } + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Web.Tests/Objects/Factory/Xml/WebObjectDefinitionReaderTests.cs b/test/Spring/Spring.Web.Tests/Objects/Factory/Xml/WebObjectDefinitionReaderTests.cs new file mode 100644 index 00000000..e72cc87b --- /dev/null +++ b/test/Spring/Spring.Web.Tests/Objects/Factory/Xml/WebObjectDefinitionReaderTests.cs @@ -0,0 +1,181 @@ +#region License + +/* + * Copyright 2002-2009 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS 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.Xml; +using NUnit.Framework; +using Spring.Collections; +using Spring.Core.IO; +using Spring.Objects.Factory.Support; +using Spring.Objects.Factory.Xml; +using Spring.TestSupport; +using Spring.Util; + +namespace Spring.Objects.Factory +{ + /// + /// + /// Erich Eichinger + [TestFixture] + public class WebObjectDefinitionReaderTests + { + public class TestWebObjectDefinitionReader : WebObjectDefinitionReader + { + public TestWebObjectDefinitionReader(string contextVirtualPath, IObjectDefinitionRegistry registry, XmlResolver resolver) + : base(contextVirtualPath, registry, resolver) + {} + } + + [Test] + public void ControlDefinitionsGetMarkedAbstract() + { + const string CONTEXTPATH = "/ContextPath/"; + const string xml = + @" + + +"; + + WebObjectFactory objectFactory = new WebObjectFactory(CONTEXTPATH, false); + TestWebObjectDefinitionReader reader = new TestWebObjectDefinitionReader(objectFactory.ContextPath, objectFactory, new XmlUrlResolver()); + + using (VirtualEnvironmentMock env = new VirtualEnvironmentMock(CONTEXTPATH + "test.aspx", null, null, CONTEXTPATH, true)) + { + env.VirtualPath2ArtifactsTable[CONTEXTPATH + "MyControl.ascx"] = typeof(Spring.Web.UI.UserControl); + + reader.LoadObjectDefinitions(new StringResource(xml)); + } + + Assert.IsTrue(objectFactory.ContainsObjectDefinition("Spring.Web.UI.UserControl")); + Assert.IsTrue(objectFactory.GetObjectDefinition("Spring.Web.UI.UserControl").IsAbstract); + } + + [Test] + public void ParsesPagePathIntoObjectNameIfNeitherIdNorNameAttributeSpecified() + { + const string CONTEXTPATH = "/ContextPath/"; + + const string xml = + @" + + + +"; + + WebObjectFactory objectFactory = new WebObjectFactory(CONTEXTPATH, false); + TestWebObjectDefinitionReader reader = new TestWebObjectDefinitionReader(objectFactory.ContextPath, objectFactory, new XmlUrlResolver()); + + using (VirtualEnvironmentMock env = new VirtualEnvironmentMock(CONTEXTPATH + "test.aspx", null, null, CONTEXTPATH, true)) + { + env.VirtualPath2ArtifactsTable[CONTEXTPATH + "MyPage.aspx"] = typeof (Spring.Web.UI.Page); + env.VirtualPath2ArtifactsTable[CONTEXTPATH + "MyControl.ascx"] = typeof (Spring.Web.UI.UserControl); + + reader.LoadObjectDefinitions(new StringResource(xml)); + } + + Assert.IsTrue(objectFactory.ContainsObjectDefinition("/MyPage.aspx")); + Assert.AreEqual(typeof(Spring.Web.UI.Page), objectFactory.GetType("/MyPage.aspx")); + Assert.IsTrue(objectFactory.ContainsObjectDefinition("Spring.Web.UI.UserControl")); + } + + [Test] + public void DoesNotGenerateObjectNameIfIdAttributeSpecified() + { + const string CONTEXTPATH = "/ContextPath/"; + const string xml = + @" + + + +"; + + WebObjectFactory objectFactory = new WebObjectFactory(CONTEXTPATH, false); + TestWebObjectDefinitionReader reader = new TestWebObjectDefinitionReader(objectFactory.ContextPath, objectFactory, new XmlUrlResolver()); + + using (VirtualEnvironmentMock env = new VirtualEnvironmentMock(CONTEXTPATH + "test.aspx", null, null, CONTEXTPATH, true)) + { + env.VirtualPath2ArtifactsTable[CONTEXTPATH + "MyPage.aspx"] = typeof (Spring.Web.UI.Page); + env.VirtualPath2ArtifactsTable[CONTEXTPATH + "MyControl.ascx"] = typeof (Spring.Web.UI.UserControl); + + reader.LoadObjectDefinitions(new StringResource(xml)); + } + + Assert.IsTrue(objectFactory.ContainsObjectDefinition("mypage")); + Assert.AreEqual(typeof(Spring.Web.UI.Page), objectFactory.GetType("mypage")); + Assert.IsTrue(objectFactory.ContainsObjectDefinition("mycontrol")); + } + + [Test] + public void DoesNotGenerateObjectNameIfNameAttributeSpecified() + { + const string CONTEXTPATH = "/ContextPath/"; + const string xml = + @" + + + +"; + + WebObjectFactory objectFactory = new WebObjectFactory(CONTEXTPATH, false); + TestWebObjectDefinitionReader reader = new TestWebObjectDefinitionReader(objectFactory.ContextPath, objectFactory, new XmlUrlResolver()); + + using (VirtualEnvironmentMock env = new VirtualEnvironmentMock(CONTEXTPATH + "test.aspx", null, null, CONTEXTPATH, true)) + { + env.VirtualPath2ArtifactsTable[CONTEXTPATH + "MyPage.aspx"] = typeof(Spring.Web.UI.Page); + env.VirtualPath2ArtifactsTable[CONTEXTPATH + "MyControl.ascx"] = typeof(Spring.Web.UI.UserControl); + + reader.LoadObjectDefinitions(new StringResource(xml)); + } + + Assert.IsTrue(objectFactory.ContainsObjectDefinition("mypage")); + Assert.AreEqual(typeof(Spring.Web.UI.Page), objectFactory.GetType("mypage")); + Assert.IsTrue(objectFactory.ContainsObjectDefinition("mycontrol")); + } + + [Test] + public void DoesNotGenerateObjectNameIfIdAndNameAttributeSpecified() + { + const string CONTEXTPATH = "/"; + const string xml = + @" + + + +"; + + WebObjectFactory objectFactory = new WebObjectFactory(CONTEXTPATH, false); + TestWebObjectDefinitionReader reader = new TestWebObjectDefinitionReader(objectFactory.ContextPath, objectFactory, new XmlUrlResolver()); + + using (VirtualEnvironmentMock env = new VirtualEnvironmentMock(CONTEXTPATH + "test.aspx", null, null, CONTEXTPATH, true)) + { + env.VirtualPath2ArtifactsTable[CONTEXTPATH + "MyPage.aspx"] = typeof(Spring.Web.UI.Page); + env.VirtualPath2ArtifactsTable[CONTEXTPATH + "MyControl.ascx"] = typeof(Spring.Web.UI.UserControl); + + reader.LoadObjectDefinitions(new StringResource(xml)); + } + + Assert.IsTrue(objectFactory.ContainsObjectDefinition("mypage")); + Assert.IsTrue(objectFactory.ContainsObject("mypageAlias")); + Assert.IsTrue(objectFactory.ContainsObjectDefinition("mycontrol")); + Assert.IsTrue(objectFactory.ContainsObject("mycontrolAlias")); + } + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Web.Tests/Spring.Web.Tests.2008.csproj b/test/Spring/Spring.Web.Tests/Spring.Web.Tests.2008.csproj index 23418874..1f227275 100644 --- a/test/Spring/Spring.Web.Tests/Spring.Web.Tests.2008.csproj +++ b/test/Spring/Spring.Web.Tests/Spring.Web.Tests.2008.csproj @@ -104,6 +104,8 @@ Code + + diff --git a/test/Spring/Spring.Web.Tests/TestSupport/VirtualEnvironmentMock.cs b/test/Spring/Spring.Web.Tests/TestSupport/VirtualEnvironmentMock.cs index f3ecb342..894c6af5 100644 --- a/test/Spring/Spring.Web.Tests/TestSupport/VirtualEnvironmentMock.cs +++ b/test/Spring/Spring.Web.Tests/TestSupport/VirtualEnvironmentMock.cs @@ -2,6 +2,9 @@ using System; using System.Collections; using System.Collections.Specialized; using System.IO; +using System.Reflection; +using System.Text; +using System.Web; using Spring.Collections; using Spring.Util; @@ -15,20 +18,30 @@ namespace Spring.TestSupport { private readonly IVirtualEnvironment _prevEnvironment; - private readonly string _currentVirtualFilePath; - private readonly string _pathInfo; + private string _currentVirtualFilePath; + private string _pathInfo; + private HttpValueCollection _query; private string _currentExecutionFilePath; private readonly string _applicationVirtualPath; private ISessionState _session = new SessionMock(); private IDictionary _requestVariables = new CaseInsensitiveHashtable(); //CollectionsUtil.CreateCaseInsensitiveHashtable(); + private NameValueCollection requestParams = new NameValueCollection(); + private IDictionary virtualPath2ArtifactsTable = new CaseInsensitiveHashtable(); - public VirtualEnvironmentMock(string currentVirtualFilePath, string pathInfo, string applicationVirtualPath, bool autoInitialize) + public VirtualEnvironmentMock(string currentVirtualFilePath, string pathInfo, string queryText, string applicationVirtualPath, bool autoInitialize) { _currentVirtualFilePath = currentVirtualFilePath; + _currentExecutionFilePath = currentVirtualFilePath; _pathInfo = (pathInfo == null || pathInfo.Length == 0) ? "" : "/" + pathInfo.TrimStart('/'); // prevent null string and ensure '/' prefixed + _query = new HttpValueCollection(queryText); _applicationVirtualPath = "/" + ("" + applicationVirtualPath).Trim('/'); if (!_applicationVirtualPath.EndsWith("/")) _applicationVirtualPath = _applicationVirtualPath + "/"; +// if (!_currentVirtualFilePath.StartsWith(_applicationVirtualPath)) +// { +// throw new ArgumentException("currentVirtualFilePath must begin with applicationVirtualPath"); +// } + _prevEnvironment = VirtualEnvironment.SetInstance(this); if (autoInitialize) { @@ -36,6 +49,11 @@ namespace Spring.TestSupport } } + public IDictionary VirtualPath2ArtifactsTable + { + get { return virtualPath2ArtifactsTable; } + } + public string ApplicationVirtualPath { get { return _applicationVirtualPath; } @@ -43,9 +61,19 @@ namespace Spring.TestSupport public string CurrentVirtualPath { - get - { - return _currentVirtualFilePath + _pathInfo; + get { return _currentVirtualFilePath + _pathInfo; } + } + + public string CurrentVirtualPathAndQuery + { + get + { + string result = _currentVirtualFilePath + _pathInfo; + if (_query.Count > 0) + { + result = result + "?" + _query.ToString(); + } + return result; } } @@ -60,14 +88,58 @@ namespace Spring.TestSupport set { this._currentExecutionFilePath = value; } } + public NameValueCollection QueryString + { + get { return _query; } + } + public string MapPath(string virtualPath) { string basePath = Path.GetDirectoryName(new Uri(GetType().Assembly.CodeBase).LocalPath); - string resultPath = WebUtils.CreateAbsolutePath(this.ApplicationVirtualPath, virtualPath); + string resultPath = WebUtils.CreateAbsolutePath(this.CurrentVirtualFilePath, virtualPath); resultPath = basePath.TrimEnd('\\') + "\\" + resultPath.Replace('/', '\\').TrimStart('\\'); return resultPath; } + public IDisposable RewritePath(string newVirtualPath, bool rebaseClientPath) + { + IDisposable ctx = new RewriteContext(CurrentVirtualPathAndQuery, false, this); + + int index = newVirtualPath.IndexOf('?'); + if (index >= 0) + { + string newQueryString = (index < (newVirtualPath.Length - 1)) ? newVirtualPath.Substring(index + 1) : string.Empty; + _query = new HttpValueCollection(newQueryString); + newVirtualPath = newVirtualPath.Substring(0, index); + } + + _currentVirtualFilePath = newVirtualPath; + + return ctx; + } + + public Type GetCompiledType(string virtualPath) + { + object o = virtualPath2ArtifactsTable[virtualPath]; + if (o == null) + throw new FileNotFoundException(virtualPath); + else if (o is Type) + return (Type) o; + else + return o.GetType(); + } + + public object CreateInstanceFromVirtualPath(string virtualPath, Type requiredBaseType) + { + object o = virtualPath2ArtifactsTable[virtualPath]; + if (o == null) + throw new FileNotFoundException(virtualPath); + else if (o is Type) + return Activator.CreateInstance((Type)o); + else + return o; + } + public ISessionState Session { get { return _session; } @@ -80,9 +152,152 @@ namespace Spring.TestSupport set { _requestVariables = value; } } + public NameValueCollection RequestParams + { + get { return requestParams; } + } + public void Dispose() { VirtualEnvironment.SetInstance(_prevEnvironment); } + + private class RewriteContext : IDisposable + { + private string originalPath; + private bool rebaseClientPath; + private VirtualEnvironmentMock runtime; + + public RewriteContext(string originalPath, bool rebaseClientPath, VirtualEnvironmentMock runtime) + { + this.originalPath = originalPath; + this.rebaseClientPath = rebaseClientPath; + this.runtime = runtime; + } + + public void Dispose() + { + if (originalPath != null) + { + this.runtime.RewritePath(originalPath, rebaseClientPath); + } + } + } + + private class HttpValueCollection : NameValueCollection + { + public HttpValueCollection(string queryText) + { + FillFromString(queryText, false, Encoding.UTF8); + } + + public override string ToString() + { + return ToString(true); + } + + private void FillFromString(string s, bool urlencoded, Encoding encoding) + { + int num = (s != null) ? s.Length : 0; + for (int i = 0; i < num; i++) + { + int startIndex = i; + int num4 = -1; + while (i < num) + { + char ch = s[i]; + if (ch == '=') + { + if (num4 < 0) + { + num4 = i; + } + } + else if (ch == '&') + { + break; + } + i++; + } + string str = null; + string str2 = null; + if (num4 >= 0) + { + str = s.Substring(startIndex, num4 - startIndex); + str2 = s.Substring(num4 + 1, (i - num4) - 1); + } + else + { + str2 = s.Substring(startIndex, i - startIndex); + } + if (urlencoded) + { + base.Add(HttpUtility.UrlDecode(str, encoding), HttpUtility.UrlDecode(str2, encoding)); + } + else + { + base.Add(str, str2); + } + if ((i == (num - 1)) && (s[i] == '&')) + { + base.Add(null, string.Empty); + } + } + } + + internal virtual string ToString(bool urlencoded) + { + StringBuilder builder = new StringBuilder(); + int count = this.Count; + for (int i = 0; i < count; i++) + { + string str3; + string key = this.GetKey(i); + if (urlencoded) + { + key = HttpUtility.UrlEncodeUnicode(key); + } + string str2 = ((key != null) && (key.Length > 0)) ? (key + "=") : ""; + ArrayList list = (ArrayList)base.BaseGet(i); + int num3 = (list != null) ? list.Count : 0; + if (i > 0) + { + builder.Append('&'); + } + if (num3 == 1) + { + builder.Append(str2); + str3 = (string)list[0]; + if (urlencoded) + { + str3 = HttpUtility.UrlEncodeUnicode(str3); + } + builder.Append(str3); + } + else if (num3 == 0) + { + builder.Append(str2); + } + else + { + for (int j = 0; j < num3; j++) + { + if (j > 0) + { + builder.Append('&'); + } + builder.Append(str2); + str3 = (string)list[j]; + if (urlencoded) + { + str3 = HttpUtility.UrlEncodeUnicode(str3); + } + builder.Append(str3); + } + } + } + return builder.ToString(); + } + } } } \ 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 index 065b11e5..b10d46d9 100644 --- a/test/Spring/Spring.Web.Tests/Util/WebUtilsTests.cs +++ b/test/Spring/Spring.Web.Tests/Util/WebUtilsTests.cs @@ -39,34 +39,6 @@ namespace Spring.Util { 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() @@ -180,7 +152,7 @@ namespace Spring.Util public void CombineVirtualPathsInRootWeb() { // emulate root website context - using( new VirtualEnvironmentMock("/somedir/some.file", null, "/", true) ) + using( new VirtualEnvironmentMock("/somedir/some.file", null, null, "/", true) ) { CombineVirtualPathsSuite( "/" ); } @@ -190,7 +162,7 @@ namespace Spring.Util public void CombineVirtualPathsInChildWeb() { // emulate child website context - using( new VirtualEnvironmentMock("/somedir/some.file", null, "/myapp", true) ) + using( new VirtualEnvironmentMock("/somedir/some.file", null, null, "/myapp", true) ) { CombineVirtualPathsSuite( "/myapp/" ); }