Fix ReferenceNode, so that it doesnt throw KeyNotFoundException when current context wasn't provided.

This commit is contained in:
Kamil Pituła
2022-03-16 10:24:31 +01:00
committed by Marko Lahma
parent e4438e6933
commit 9a4577389c
2 changed files with 226 additions and 92 deletions

View File

@@ -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
{
/// <summary>
/// Represents a reference to a Spring-managed object.
/// </summary>
/// <author>Aleksandar Seovic</author>
[Serializable]
public class ReferenceNode : BaseNode
{
/// <summary>
/// Create a new instance
/// </summary>
public ReferenceNode():base()
{
}
/// <summary>
/// Represents a reference to a Spring-managed object.
/// </summary>
/// <author>Aleksandar Seovic</author>
[Serializable]
public class ReferenceNode : BaseNode
{
/// <summary>
/// Create a new instance
/// </summary>
public ReferenceNode() { }
/// <summary>
/// Create a new instance from SerializationInfo
/// </summary>
protected ReferenceNode(SerializationInfo info, StreamingContext context)
: base(info, context)
{
}
/// <summary>
/// Returns a value for the integer literal node.
/// </summary>
/// <param name="context">Context to evaluate expressions against.</param>
/// <param name="evalContext">Current expression evaluation context.</param>
/// <returns>Node's value.</returns>
protected override object Get(object context, EvaluationContext evalContext)
{
IApplicationContext ctx;
string objectName;
/// <summary>
/// Create a new instance from SerializationInfo
/// </summary>
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;
/// <inheritdoc />
/// <summary>
/// Returns a value for the integer literal node.
/// </summary>
/// <param name="context">Context to evaluate expressions against.</param>
/// <param name="evalContext">Current expression evaluation context.</param>
/// <returns>Node's value.</returns>
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);
}
}
}

View File

@@ -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
{
/// <summary>
///
/// </summary>
/// <author>Erich Eichinger</author>
[TestFixture]
public class ReferenceNodeTests
{
public class MyTestObject
{
public object MyField;
}
/// <summary>
///
/// </summary>
/// <author>Erich Eichinger</author>
[TestFixture]
public class ReferenceNodeTests
{
private class MyTestObject
{
public object MyField { get; set; }
}
[Test]
public void DoesntCallContextRegistryForLocalObjectFactoryReferences()
{
string xml = string.Format(
@"<?xml version='1.0' encoding='UTF-8' ?>
[TearDown]
public void TearDown() => ContextRegistry.Clear();
[Test]
public void DoesNotCallContextRegistryForLocalObjectFactoryReferences()
{
var xml = $@"<?xml version='1.0' encoding='UTF-8' ?>
<objects xmlns='http://www.springframework.net'>
<object id='foo' type='{0}'>
<object id='foo' type='{typeof(MyTestObject).AssemblyQualifiedName}'>
<property name='MyField' expression='@(theObject)' />
</object>
</objects>"
, typeof(MyTestObject).AssemblyQualifiedName
);
</objects>";
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 = $@"<?xml version='1.0' encoding='UTF-8' ?>
<objects xmlns='http://www.springframework.net'>
<object id='theObject' type='{typeof(MyTestObject).AssemblyQualifiedName}'/>
</objects>";
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<string, object>());
Assert.That(value, Is.SameAs(expectedObject));
}
[Test]
public void ThrowsApplicationContextException_WhenContextNotRegistered()
{
var defaultXml = $@"<?xml version='1.0' encoding='UTF-8' ?>
<objects xmlns='http://www.springframework.net'>
<object id='theObject' type='{typeof(MyTestObject).AssemblyQualifiedName}'/>
</objects>";
var defaultContext = GetContextFromXmlString(defaultXml, AbstractApplicationContext.DefaultRootContextName);
ContextRegistry.RegisterContext(defaultContext);
var expression = Expression.Parse("@(anotherContext:theObject).Value");
void Get() => expression.GetValue(null, new Dictionary<string, object>());
Assert.That(Get, Throws.InstanceOf<ApplicationContextException>());
}
[Test]
public void WhenContextNameSpecifiedInExpression_UseThatContext()
{
const string anotherContextName = "AnotherContext";
var defaultXml = $@"<?xml version='1.0' encoding='UTF-8' ?>
<objects xmlns='http://www.springframework.net'>
<object id='theObject' type='{typeof(MyTestObject).AssemblyQualifiedName}'/>
</objects>";
var anotherXml = $@"<?xml version='1.0' encoding='UTF-8' ?>
<objects xmlns='http://www.springframework.net'>
<object id='theObject' type='{typeof(MyTestObject).AssemblyQualifiedName}'/>
</objects>";
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<string, object>());
Assert.That(resolvedObject, Is.SameAs(expectedObject));
}
[Test]
public void UseObjectFactoryFromVariables()
{
const string anotherContextName = "AnotherContext";
var defaultXml = $@"<?xml version='1.0' encoding='UTF-8' ?>
<objects xmlns='http://www.springframework.net'>
<object id='theObject' type='{typeof(MyTestObject).AssemblyQualifiedName}'/>
</objects>";
var anotherXml = $@"<?xml version='1.0' encoding='UTF-8' ?>
<objects xmlns='http://www.springframework.net'>
<object id='theObject' type='{typeof(MyTestObject).AssemblyQualifiedName}'/>
</objects>";
var defaultContext = GetContextFromXmlString(defaultXml, AbstractApplicationContext.DefaultRootContextName);
ContextRegistry.RegisterContext(defaultContext);
var anotherContext = GetContextFromXmlString(anotherXml, anotherContextName);
var variables = new Dictionary<string, object>
{
[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 = $@"<?xml version='1.0' encoding='UTF-8' ?>
<objects xmlns='http://www.springframework.net'>
<object id='theObject' type='{typeof(MyTestObject).AssemblyQualifiedName}'/>
</objects>";
var defaultContext = GetContextFromXmlString(defaultXml, AbstractApplicationContext.DefaultRootContextName);
ContextRegistry.RegisterContext(defaultContext);
var variables = new Dictionary<string, object>
{
[Expression.ReservedVariableNames.RESERVEDPREFIX + "CurrentObjectFactory"] = new object()
};
var expression = Expression.Parse("@(theObject)");
void Get() => expression.GetValue(null, variables);
Assert.That(Get, Throws.InstanceOf<InvalidCastException>());
}
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 };
}
}
}