From 9a4577389c7d6b75055a0945732d010b2cf90716 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20Pitu=C5=82a?= Date: Wed, 16 Mar 2022 10:24:31 +0100 Subject: [PATCH] Fix ReferenceNode, so that it doesnt throw KeyNotFoundException when current context wasn't provided. --- .../Spring.Core/Expressions/ReferenceNode.cs | 130 ++++++------ .../Expressions/ReferenceNodeTests.cs | 188 +++++++++++++++--- 2 files changed, 226 insertions(+), 92 deletions(-) diff --git a/src/Spring/Spring.Core/Expressions/ReferenceNode.cs b/src/Spring/Spring.Core/Expressions/ReferenceNode.cs index 27f4c4f3..971be72c 100644 --- a/src/Spring/Spring.Core/Expressions/ReferenceNode.cs +++ b/src/Spring/Spring.Core/Expressions/ReferenceNode.cs @@ -18,78 +18,80 @@ #endregion -using System; -using System.Runtime.Serialization; using Spring.Expressions; using Spring.Objects.Factory; +using System; +using System.Runtime.Serialization; namespace Spring.Context.Support { - /// - /// Represents a reference to a Spring-managed object. - /// - /// Aleksandar Seovic - [Serializable] - public class ReferenceNode : BaseNode - { - /// - /// Create a new instance - /// - public ReferenceNode():base() - { - } + /// + /// Represents a reference to a Spring-managed object. + /// + /// Aleksandar Seovic + [Serializable] + public class ReferenceNode : BaseNode + { + /// + /// Create a new instance + /// + public ReferenceNode() { } - /// - /// Create a new instance from SerializationInfo - /// - protected ReferenceNode(SerializationInfo info, StreamingContext context) - : base(info, context) - { - } - - /// - /// Returns a value for the integer literal node. - /// - /// Context to evaluate expressions against. - /// Current expression evaluation context. - /// Node's value. - protected override object Get(object context, EvaluationContext evalContext) - { - IApplicationContext ctx; - string objectName; + /// + /// Create a new instance from SerializationInfo + /// + protected ReferenceNode(SerializationInfo info, StreamingContext context) + : base(info, context) { } - if (this.getNumberOfChildren() == 2) - { - string contextName = this.getFirstChild().getText(); - objectName = this.getFirstChild().getNextSibling().getText(); - ctx = ContextRegistry.GetContext(contextName); - if (ctx == null) - { - throw new ArgumentException(string.Format("Context '{0}' is not registered.", contextName)); - } - } - else - { - objectName = this.getFirstChild().getText(); - IObjectFactory currentObjectFactory = (evalContext.Variables != null) - ? (IObjectFactory)evalContext.Variables[Expression.ReservedVariableNames.CurrentObjectFactory] - : null; + /// + /// + /// Returns a value for the integer literal node. + /// + /// Context to evaluate expressions against. + /// Current expression evaluation context. + /// Node's value. + protected override object Get(object context, EvaluationContext evalContext) + { + var objectName = ResolveNames(out var contextName); - // this is a local reference within an object factory - if (currentObjectFactory != null) - { - return currentObjectFactory.GetObject(objectName); - } + var sourceContext = SelectSourceContext(evalContext, contextName); - // else lookup in default context - ctx = ContextRegistry.GetContext(); - if (ctx == null) - { - throw new ArgumentException("No context registered."); - } - } + return sourceContext.GetObject(objectName); + } - return ctx.GetObject(objectName); - } - } + private string ResolveNames(out string contextName) + { + var hasContextDefined = getNumberOfChildren() == 2; + + if (hasContextDefined) + { + contextName = getFirstChild().getText(); + return getFirstChild().getNextSibling().getText(); + } + + contextName = null; + return getFirstChild().getText(); + } + + private static IObjectFactory SelectSourceContext(EvaluationContext evalContext, string contextName) + { + if (contextName != null) + return ContextRegistry.GetContext(contextName) ?? throw new ArgumentException($"Context '{contextName}' is not registered."); + + if (TryGetFromCurrentContext(evalContext, out var currentObjectFactory)) + return (IObjectFactory)currentObjectFactory; + + return ContextRegistry.GetContext() ?? throw new ArgumentException("No context registered."); + } + + private static bool TryGetFromCurrentContext(EvaluationContext evalContext, out object currentObjectFactory) + { + currentObjectFactory = null; + + if (evalContext.Variables is null) + return false; + + return evalContext.Variables.TryGetValue(Expression.ReservedVariableNames.CurrentObjectFactory, out currentObjectFactory); + } + } } \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Expressions/ReferenceNodeTests.cs b/test/Spring/Spring.Core.Tests/Expressions/ReferenceNodeTests.cs index 80844794..39cfe1eb 100644 --- a/test/Spring/Spring.Core.Tests/Expressions/ReferenceNodeTests.cs +++ b/test/Spring/Spring.Core.Tests/Expressions/ReferenceNodeTests.cs @@ -20,46 +20,178 @@ #region Imports -using System.Text; +using System; + using NUnit.Framework; +using Spring.Context; +using Spring.Context.Support; using Spring.Core.IO; using Spring.Objects.Factory.Xml; +using System.Collections.Generic; +using System.Text; #endregion namespace Spring.Expressions { - /// - /// - /// - /// Erich Eichinger - [TestFixture] - public class ReferenceNodeTests - { - public class MyTestObject - { - public object MyField; - } + /// + /// + /// + /// Erich Eichinger + [TestFixture] + public class ReferenceNodeTests + { + private class MyTestObject + { + public object MyField { get; set; } + } - [Test] - public void DoesntCallContextRegistryForLocalObjectFactoryReferences() - { - string xml = string.Format( - @" + [TearDown] + public void TearDown() => ContextRegistry.Clear(); + + [Test] + public void DoesNotCallContextRegistryForLocalObjectFactoryReferences() + { + var xml = $@" - + -" - , typeof(MyTestObject).AssemblyQualifiedName - ); +"; - XmlObjectFactory of = new XmlObjectFactory(new StringResource(xml, Encoding.UTF8)); - object theObject = new object(); - of.RegisterSingleton("theObject", theObject); + var objectFactory = new XmlObjectFactory(new StringResource(xml, Encoding.UTF8)); + var theObject = new object(); + objectFactory.RegisterSingleton("theObject", theObject); - MyTestObject to = (MyTestObject) of.GetObject("foo"); - Assert.AreSame( theObject, to.MyField ); - } - } + var to = (MyTestObject)objectFactory.GetObject("foo"); + Assert.That(theObject, Is.SameAs(to.MyField)); + } + + [Test] + public void UseDefaultContextRegistryWhenNoContextProvided() + { + var defaultXml = $@" + + +"; + + var defaultContext = GetContextFromXmlString(defaultXml, AbstractApplicationContext.DefaultRootContextName); + ContextRegistry.RegisterContext(defaultContext); + + var expectedObject = defaultContext.GetObject("theObject"); + + var expression = Expression.Parse("@(theObject)"); + var value = expression.GetValue(null, new Dictionary()); + + Assert.That(value, Is.SameAs(expectedObject)); + } + + [Test] + public void ThrowsApplicationContextException_WhenContextNotRegistered() + { + var defaultXml = $@" + + +"; + + var defaultContext = GetContextFromXmlString(defaultXml, AbstractApplicationContext.DefaultRootContextName); + ContextRegistry.RegisterContext(defaultContext); + + var expression = Expression.Parse("@(anotherContext:theObject).Value"); + void Get() => expression.GetValue(null, new Dictionary()); + + Assert.That(Get, Throws.InstanceOf()); + } + + [Test] + public void WhenContextNameSpecifiedInExpression_UseThatContext() + { + const string anotherContextName = "AnotherContext"; + + var defaultXml = $@" + + +"; + + var anotherXml = $@" + + +"; + + var defaultContext = GetContextFromXmlString(defaultXml, AbstractApplicationContext.DefaultRootContextName); + ContextRegistry.RegisterContext(defaultContext); + + var anotherContext = GetContextFromXmlString(anotherXml, anotherContextName); + ContextRegistry.RegisterContext(anotherContext); + + var expectedObject = anotherContext.GetObject("theObject"); + + var expression = Expression.Parse($"@({anotherContextName}:theObject)"); + var resolvedObject = expression.GetValue(null, new Dictionary()); + + Assert.That(resolvedObject, Is.SameAs(expectedObject)); + } + + [Test] + public void UseObjectFactoryFromVariables() + { + const string anotherContextName = "AnotherContext"; + + var defaultXml = $@" + + +"; + + var anotherXml = $@" + + +"; + + var defaultContext = GetContextFromXmlString(defaultXml, AbstractApplicationContext.DefaultRootContextName); + ContextRegistry.RegisterContext(defaultContext); + + var anotherContext = GetContextFromXmlString(anotherXml, anotherContextName); + var variables = new Dictionary + { + [Expression.ReservedVariableNames.RESERVEDPREFIX + "CurrentObjectFactory"] = anotherContext.ObjectFactory + }; + var expectedObject = anotherContext.GetObject("theObject"); + + var expression = Expression.Parse("@(theObject)"); + var resolvedObject = expression.GetValue(null, variables); + + Assert.That(resolvedObject, Is.SameAs(expectedObject)); + } + + [Test] + public void ShouldThrowException_WhenFactoryProvidedInVariables_IsNotOfTypeIObjectFactory() + { + var defaultXml = $@" + + +"; + + var defaultContext = GetContextFromXmlString(defaultXml, AbstractApplicationContext.DefaultRootContextName); + ContextRegistry.RegisterContext(defaultContext); + + var variables = new Dictionary + { + [Expression.ReservedVariableNames.RESERVEDPREFIX + "CurrentObjectFactory"] = new object() + }; + + var expression = Expression.Parse("@(theObject)"); + + void Get() => expression.GetValue(null, variables); + + Assert.That(Get, Throws.InstanceOf()); + } + + private static GenericApplicationContext GetContextFromXmlString(string xmlString, string contextName) + { + var stringResource = new StringResource(xmlString, Encoding.UTF8); + var objectFactory = new XmlObjectFactory(stringResource); + + return new GenericApplicationContext(objectFactory) { Name = contextName }; + } + } } \ No newline at end of file