From 18b7d1eedd486f46b5bee29ed72a1cc236198d0e Mon Sep 17 00:00:00 2001
From: eeichinger
Date: Wed, 29 Jul 2009 20:59:47 +0000
Subject: [PATCH] SPRNET-1243 SPRNET-1167
---
.../Config/CommandLineArgsVariableSource.cs | 15 ++
.../Config/ConfigSectionVariableSource.cs | 15 ++
.../Config/ConnectionStringsVariableSource.cs | 23 ++-
.../Config/DictionaryVariableSource.cs | 135 ++++++++++++++
.../Config/EnvironmentVariableSource.cs | 11 ++
.../Objects/Factory/Config/IVariableSource.cs | 10 +-
.../Factory/Config/ObjectDefinitionVisitor.cs | 52 +++---
.../Config/PropertyFileVariableSource.cs | 15 ++
.../Config/PropertyPlaceholderConfigurer.cs | 43 +++--
.../Factory/Config/RegistryVariableSource.cs | 12 ++
.../Config/SpecialFolderVariableSource.cs | 11 ++
.../Factory/Config/VariableAccessor.cs | 6 +-
.../Config/VariablePlaceholderConfigurer.cs | 169 +++++++++++-------
.../Spring.Core/Spring.Core.2008.csproj | 1 +
.../Spring.Core/Util/CollectionUtils.cs | 32 +++-
.../Expressions/ExpressionEvaluatorTests.cs | 12 ++
.../Config/ObjectDefinitionVisitorTests.cs | 56 ++----
.../Factory/Config/VariableAccessorTests.cs | 18 +-
.../VariablePlaceholderConfigurerTests.cs | 126 ++++++++++---
.../Util/CollectionUtilsTests.cs | 7 +-
.../ConfigSectionSessionScopeSettingsTests.cs | 18 +-
21 files changed, 562 insertions(+), 225 deletions(-)
create mode 100644 src/Spring/Spring.Core/Objects/Factory/Config/DictionaryVariableSource.cs
diff --git a/src/Spring/Spring.Core/Objects/Factory/Config/CommandLineArgsVariableSource.cs b/src/Spring/Spring.Core/Objects/Factory/Config/CommandLineArgsVariableSource.cs
index 1a9fd8c4..e720f8f1 100644
--- a/src/Spring/Spring.Core/Objects/Factory/Config/CommandLineArgsVariableSource.cs
+++ b/src/Spring/Spring.Core/Objects/Factory/Config/CommandLineArgsVariableSource.cs
@@ -87,6 +87,21 @@ namespace Spring.Objects.Factory.Config
set { valueSeparator = value; }
}
+ ///
+ /// Before requesting a variable resolution, a client should
+ /// ask, whether the source can resolve a particular variable name.
+ ///
+ /// the name of the variable to resolve
+ /// true if the variable can be resolved, false otherwise
+ public bool CanResolveVariable(string name)
+ {
+ if (arguments == null)
+ {
+ InitArguments();
+ }
+ return arguments.Contains(name);
+ }
+
///
/// Resolves variable value for the specified variable name.
///
diff --git a/src/Spring/Spring.Core/Objects/Factory/Config/ConfigSectionVariableSource.cs b/src/Spring/Spring.Core/Objects/Factory/Config/ConfigSectionVariableSource.cs
index 75e584d2..02e44559 100644
--- a/src/Spring/Spring.Core/Objects/Factory/Config/ConfigSectionVariableSource.cs
+++ b/src/Spring/Spring.Core/Objects/Factory/Config/ConfigSectionVariableSource.cs
@@ -92,6 +92,21 @@ namespace Spring.Objects.Factory.Config
set { sectionNames = new string[] { value }; }
}
+ ///
+ /// Before requesting a variable resolution, a client should
+ /// ask, whether the source can resolve a particular variable name.
+ ///
+ /// the name of the variable to resolve
+ /// true if the variable can be resolved, false otherwise
+ public bool CanResolveVariable(string name)
+ {
+ if (variables == null)
+ {
+ InitVariables();
+ }
+ return CollectionUtils.Contains(variables.AllKeys, name);
+ }
+
///
/// Resolves variable value for the specified variable name.
///
diff --git a/src/Spring/Spring.Core/Objects/Factory/Config/ConnectionStringsVariableSource.cs b/src/Spring/Spring.Core/Objects/Factory/Config/ConnectionStringsVariableSource.cs
index 6a79bc79..64c8e099 100644
--- a/src/Spring/Spring.Core/Objects/Factory/Config/ConnectionStringsVariableSource.cs
+++ b/src/Spring/Spring.Core/Objects/Factory/Config/ConnectionStringsVariableSource.cs
@@ -20,6 +20,7 @@
#endregion
using System;
+using System.Collections;
using System.Collections.Specialized;
using System.Configuration;
using Spring.Util;
@@ -53,8 +54,24 @@ namespace Spring.Objects.Factory.Config
[Serializable]
public class ConnectionStringsVariableSource : IVariableSource
{
- private NameValueCollection variables;
+ private Hashtable variables;
+
+ ///
+ /// Before requesting a variable resolution, a client should
+ /// ask, whether the source can resolve a particular variable name.
+ ///
+ /// the name of the variable to resolve
+ /// true if the variable can be resolved, false otherwise
+ public bool CanResolveVariable(string name)
+ {
+ if (variables == null)
+ {
+ InitVariables();
+ }
+ return variables.Contains(name);
+ }
+
///
/// Resolves variable value for the specified variable name.
///
@@ -70,7 +87,7 @@ namespace Spring.Objects.Factory.Config
{
InitVariables();
}
- return variables.Get(name);
+ return (string) variables[name];
}
///
@@ -79,7 +96,7 @@ namespace Spring.Objects.Factory.Config
///
private void InitVariables()
{
- variables = new NameValueCollection();
+ variables = CollectionsUtil.CreateCaseInsensitiveHashtable();
ConnectionStringSettingsCollection settings = ConfigurationManager.ConnectionStrings;
foreach (ConnectionStringSettings setting in settings)
{
diff --git a/src/Spring/Spring.Core/Objects/Factory/Config/DictionaryVariableSource.cs b/src/Spring/Spring.Core/Objects/Factory/Config/DictionaryVariableSource.cs
new file mode 100644
index 00000000..d45dfd3b
--- /dev/null
+++ b/src/Spring/Spring.Core/Objects/Factory/Config/DictionaryVariableSource.cs
@@ -0,0 +1,135 @@
+#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.Collections.Specialized;
+
+namespace Spring.Objects.Factory.Config
+{
+ ///
+ /// A very simple, hashtable-based implementation of
+ ///
+ /// Erich Eichinger
+ public class DictionaryVariableSource : IVariableSource
+ {
+ private readonly Hashtable variables;
+
+ ///
+ /// Creates a new, empty variable source
+ ///
+ public DictionaryVariableSource()
+ :this(null, true)
+ {
+ }
+
+ ///
+ /// Creates a new, empty and case-insensitive variable source
+ ///
+ public DictionaryVariableSource(bool ignoreCase)
+ :this(null, ignoreCase)
+ {
+ }
+
+ ///
+ /// Create a new variable source from a list of paired string values.
+ ///
+ ///
+ ///
+ /// The example below shows, how the dictionary is filled with { 'key1', 'value1' }, { 'key2', 'value2' } pairs:
+ ///
+ /// new DictionaryVariableSource( new string[] { "key1", "value1", "key2", "value2" } )
+ ///
+ ///
+ ///
+ /// the argument list containing pairs, or null
+ public DictionaryVariableSource(params string[] args)
+ :this(true)
+ {
+ if (args != null)
+ {
+ for (int i = 0; i < args.Length; i += 2)
+ {
+ Add(args[i], args[i + 1]);
+ }
+ }
+ }
+
+ ///
+ /// Creates a new variable source, reading values from another dictionary
+ /// and converting them to strings if necessary
+ ///
+ public DictionaryVariableSource(IDictionary dictionary, bool ignoreCase)
+ {
+ if (ignoreCase)
+ {
+ variables = CollectionsUtil.CreateCaseInsensitiveHashtable();
+ }
+ else
+ {
+ variables = new Hashtable();
+ }
+
+ if (dictionary != null)
+ {
+ foreach (DictionaryEntry entry in dictionary)
+ {
+ string key = "" + entry.Key;
+ string value = entry.Value != null ? "" + entry.Value : null;
+
+ variables[key] = value;
+ }
+ }
+ }
+
+ ///
+ /// Adds a key/value pair
+ ///
+ /// this dictionary. allows for fluent config
+ public DictionaryVariableSource Add(string key, string value)
+ {
+ variables.Add(key, value);
+ return this;
+ }
+
+ ///
+ /// Before requesting a variable resolution, a client should
+ /// ask, whether the source can resolve a particular variable name.
+ ///
+ /// the name of the variable to resolve
+ /// true if the variable can be resolved, false otherwise
+ public bool CanResolveVariable(string name)
+ {
+ return variables.ContainsKey(name);
+ }
+
+ ///
+ /// Performs a variable name lookup
+ ///
+ public string ResolveVariable(string name)
+ {
+ if (!variables.ContainsKey(name))
+ {
+ throw new ArgumentException(string.Format("variable '{0}' cannot be resolved", name));
+ }
+ return (string)variables[name];
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Spring/Spring.Core/Objects/Factory/Config/EnvironmentVariableSource.cs b/src/Spring/Spring.Core/Objects/Factory/Config/EnvironmentVariableSource.cs
index b94170a0..5891e39a 100644
--- a/src/Spring/Spring.Core/Objects/Factory/Config/EnvironmentVariableSource.cs
+++ b/src/Spring/Spring.Core/Objects/Factory/Config/EnvironmentVariableSource.cs
@@ -30,6 +30,17 @@ namespace Spring.Objects.Factory.Config
[Serializable]
public class EnvironmentVariableSource : IVariableSource
{
+ ///
+ /// Before requesting a variable resolution, a client should
+ /// ask, whether the source can resolve a particular variable name.
+ ///
+ /// the name of the variable to resolve
+ /// true if the variable can be resolved, false otherwise
+ public bool CanResolveVariable(string name)
+ {
+ return (Environment.GetEnvironmentVariable(name) != null);
+ }
+
///
/// Resolves variable value for the specified variable name.
///
diff --git a/src/Spring/Spring.Core/Objects/Factory/Config/IVariableSource.cs b/src/Spring/Spring.Core/Objects/Factory/Config/IVariableSource.cs
index 4fe30385..e96d9b8d 100644
--- a/src/Spring/Spring.Core/Objects/Factory/Config/IVariableSource.cs
+++ b/src/Spring/Spring.Core/Objects/Factory/Config/IVariableSource.cs
@@ -34,7 +34,7 @@ namespace Spring.Objects.Factory.Config
///
/// Users can always write their own variable sources implementations,
/// that will allow them to load variable values from the database or
- /// other proprietary data source.
+ /// other proprietary data source.
///
///
///
@@ -45,6 +45,14 @@ namespace Spring.Objects.Factory.Config
/// Aleksandar Seovic
public interface IVariableSource
{
+ ///
+ /// Before requesting a variable resolution, a client should
+ /// ask, whether the source can resolve a particular variable name.
+ ///
+ /// the name of the variable to resolve
+ /// true if the variable can be resolved, false otherwise
+ bool CanResolveVariable(string name);
+
///
/// Resolves variable value for the specified variable name.
///
diff --git a/src/Spring/Spring.Core/Objects/Factory/Config/ObjectDefinitionVisitor.cs b/src/Spring/Spring.Core/Objects/Factory/Config/ObjectDefinitionVisitor.cs
index 53ceb048..7c50ddc2 100644
--- a/src/Spring/Spring.Core/Objects/Factory/Config/ObjectDefinitionVisitor.cs
+++ b/src/Spring/Spring.Core/Objects/Factory/Config/ObjectDefinitionVisitor.cs
@@ -44,17 +44,18 @@ namespace Spring.Objects.Factory.Config
/// Mark Pollack
public class ObjectDefinitionVisitor
{
- private IVariableSource variableSource;
+ public delegate string ResolveHandler(string rawStringValue);
+ private readonly ResolveHandler resolveHandler;
///
/// Initializes a new instance of the class,
/// applying the specified IVariableSource to all object metadata values.
///
- /// The variable source.
- public ObjectDefinitionVisitor(IVariableSource variableSource)
+ /// The handler to be called for resolving variables contained in a string.
+ public ObjectDefinitionVisitor(ResolveHandler resolveHandler)
{
- this.variableSource = variableSource;
+ this.resolveHandler = resolveHandler;
}
///
@@ -95,7 +96,7 @@ namespace Spring.Objects.Factory.Config
string objectTypeName = objectDefinition.ObjectTypeName;
if (objectTypeName != null)
{
- string resolvedName = ResolveStringValue(objectTypeName).ToString();
+ string resolvedName = ResolveStringValue(objectTypeName);
if (!objectTypeName.Equals(resolvedName))
{
objectDefinition.ObjectTypeName = resolvedName;
@@ -197,7 +198,7 @@ namespace Spring.Objects.Factory.Config
{
RuntimeObjectReference ror = (RuntimeObjectReference)value;
//name has to be of string type.
- string newObjectName = ResolveStringValue(ror.ObjectName).ToString();
+ string newObjectName = ResolveStringValue(ror.ObjectName);
if (!newObjectName.Equals(ror.ObjectName))
{
return new RuntimeObjectReference(newObjectName);
@@ -225,7 +226,7 @@ namespace Spring.Objects.Factory.Config
String stringValue = typedStringValue.Value;
if (stringValue != null)
{
- String visitedString = ResolveStringValue(stringValue).ToString();
+ String visitedString = ResolveStringValue(stringValue);
typedStringValue.Value = visitedString;
}
}
@@ -236,7 +237,7 @@ namespace Spring.Objects.Factory.Config
else if (value is ExpressionHolder)
{
ExpressionHolder holder = (ExpressionHolder)value;
- string newExpressionString = ResolveStringValue(holder.ExpressionString).ToString();
+ string newExpressionString = ResolveStringValue(holder.ExpressionString);
return new ExpressionHolder(newExpressionString);
}
return value;
@@ -251,7 +252,7 @@ namespace Spring.Objects.Factory.Config
string elementTypeName = listVal.ElementTypeName;
if (elementTypeName != null)
{
- string resolvedName = ResolveStringValue(elementTypeName).ToString();
+ string resolvedName = ResolveStringValue(elementTypeName);
if (!elementTypeName.Equals(resolvedName))
{
listVal.ElementTypeName = resolvedName;
@@ -278,7 +279,7 @@ namespace Spring.Objects.Factory.Config
string elementTypeName = setVal.ElementTypeName;
if (elementTypeName != null)
{
- string resolvedName = ResolveStringValue(elementTypeName).ToString();
+ string resolvedName = ResolveStringValue(elementTypeName);
if (!elementTypeName.Equals(resolvedName))
{
setVal.ElementTypeName = resolvedName;
@@ -306,7 +307,7 @@ namespace Spring.Objects.Factory.Config
string keyTypeName = dictVal.KeyTypeName;
if (keyTypeName != null)
{
- string resolvedName = ResolveStringValue(keyTypeName).ToString();
+ string resolvedName = ResolveStringValue(keyTypeName);
if (!keyTypeName.Equals(resolvedName))
{
dictVal.KeyTypeName = resolvedName;
@@ -316,7 +317,7 @@ namespace Spring.Objects.Factory.Config
string valueTypeName = dictVal.ValueTypeName;
if (valueTypeName != null)
{
- string resolvedName = ResolveStringValue(valueTypeName).ToString();
+ string resolvedName = ResolveStringValue(valueTypeName);
if (!valueTypeName.Equals(resolvedName))
{
dictVal.ValueTypeName = resolvedName;
@@ -357,23 +358,20 @@ namespace Spring.Objects.Factory.Config
}
///
- /// Looks up the value of the given variable name in the configured .
- ///
- /// The name of the variable to be looked up
- ///
- /// The value of this variable, as returned from the passed
- /// into the constructor
- ///
- /// If no has been configured.
- protected virtual object ResolveStringValue(string variableName)
+ /// calls the to resolve any variables contained in the raw string.
+ ///
+ /// the raw string value containing variable placeholders to be resolved
+ /// If no has been configured.
+ /// the resolved string, having variables being replaced, if any
+ protected virtual string ResolveStringValue(string rawStringValue)
{
- if (variableSource == null)
+ if (resolveHandler == null)
{
- throw new InvalidOperationException("No IVariableSource specified - pass an instance " +
- "of this object into the constructor or override the 'ResolveStringValue' method");
- }
- return variableSource.ResolveVariable(variableName);
+ throw new InvalidOperationException("No resolveHandler specified - pass an instance " +
+ "into the constructor or override the 'ResolveStringValue' method");
+ }
+
+ return resolveHandler(rawStringValue);
}
-
}
}
\ No newline at end of file
diff --git a/src/Spring/Spring.Core/Objects/Factory/Config/PropertyFileVariableSource.cs b/src/Spring/Spring.Core/Objects/Factory/Config/PropertyFileVariableSource.cs
index 69fdce31..e4267ed5 100644
--- a/src/Spring/Spring.Core/Objects/Factory/Config/PropertyFileVariableSource.cs
+++ b/src/Spring/Spring.Core/Objects/Factory/Config/PropertyFileVariableSource.cs
@@ -64,6 +64,21 @@ namespace Spring.Objects.Factory.Config
set { locations = new IResource[] { value} ;}
}
+ ///
+ /// Before requesting a variable resolution, a client should
+ /// ask, whether the source can resolve a particular variable name.
+ ///
+ /// the name of the variable to resolve
+ /// true if the variable can be resolved, false otherwise
+ public bool CanResolveVariable(string name)
+ {
+ if (properties == null)
+ {
+ InitProperties();
+ }
+ return properties.Contains(name);
+ }
+
///
/// Resolves variable value for the specified variable name.
///
diff --git a/src/Spring/Spring.Core/Objects/Factory/Config/PropertyPlaceholderConfigurer.cs b/src/Spring/Spring.Core/Objects/Factory/Config/PropertyPlaceholderConfigurer.cs
index 9256ca3d..c5656826 100644
--- a/src/Spring/Spring.Core/Objects/Factory/Config/PropertyPlaceholderConfigurer.cs
+++ b/src/Spring/Spring.Core/Objects/Factory/Config/PropertyPlaceholderConfigurer.cs
@@ -225,11 +225,10 @@ namespace Spring.Objects.Factory.Config
///
/// If an error occured.
///
- protected override void ProcessProperties(
- IConfigurableListableObjectFactory factory, NameValueCollection props)
- {
- IVariableSource variableSource = new PlaceholderResolvingStringVariableSource(this, props);
- ObjectDefinitionVisitor visitor = new ObjectDefinitionVisitor(variableSource);
+ protected override void ProcessProperties(IConfigurableListableObjectFactory factory, NameValueCollection props)
+ {
+ PlaceholderResolveHandlerAdapter resolveAdapter = new PlaceholderResolveHandlerAdapter(this, props);
+ ObjectDefinitionVisitor visitor = new ObjectDefinitionVisitor(new ObjectDefinitionVisitor.ResolveHandler(resolveAdapter.ParseAndResolveVariables));
string[] objectDefinitionNames = factory.GetObjectDefinitionNames();
for (int i = 0; i < objectDefinitionNames.Length; ++i)
@@ -397,26 +396,26 @@ namespace Spring.Objects.Factory.Config
{
return props[placeholder];
}
- }
- #region Helper class
+ #region Helper class
- internal class PlaceholderResolvingStringVariableSource : IVariableSource
- {
- private PropertyPlaceholderConfigurer ppc;
- private NameValueCollection props;
-
- public PlaceholderResolvingStringVariableSource(PropertyPlaceholderConfigurer outerPPC, NameValueCollection props)
+ private class PlaceholderResolveHandlerAdapter
{
- ppc = outerPPC;
- this.props = props;
+ private readonly PropertyPlaceholderConfigurer ppc;
+ private readonly NameValueCollection props;
+
+ public PlaceholderResolveHandlerAdapter(PropertyPlaceholderConfigurer outerPPC, NameValueCollection props)
+ {
+ ppc = outerPPC;
+ this.props = props;
+ }
+
+ public string ParseAndResolveVariables(string name)
+ {
+ return ppc.ParseString(props, name, new HashedSet());
+ }
}
- public string ResolveVariable(string name)
- {
- return ppc.ParseString(props, name, new HashedSet());
- }
- }
-
- #endregion
+ #endregion
+ }
}
\ No newline at end of file
diff --git a/src/Spring/Spring.Core/Objects/Factory/Config/RegistryVariableSource.cs b/src/Spring/Spring.Core/Objects/Factory/Config/RegistryVariableSource.cs
index 362f5a51..05c21b8b 100644
--- a/src/Spring/Spring.Core/Objects/Factory/Config/RegistryVariableSource.cs
+++ b/src/Spring/Spring.Core/Objects/Factory/Config/RegistryVariableSource.cs
@@ -30,6 +30,7 @@ namespace Spring.Objects.Factory.Config
/// Aleksandar Seovic
public class RegistryVariableSource : IVariableSource
{
+ private static readonly object NULL = new object();
private RegistryKey key;
///
@@ -44,6 +45,17 @@ namespace Spring.Objects.Factory.Config
set { key = value; }
}
+ ///
+ /// Before requesting a variable resolution, a client should
+ /// ask, whether the source can resolve a particular variable name.
+ ///
+ /// the name of the variable to resolve
+ /// true if the variable can be resolved, false otherwise
+ public bool CanResolveVariable(string name)
+ {
+ return (key != null && key.GetValue(name, NULL) != NULL);
+ }
+
///
/// Resolves variable value for the specified variable name.
///
diff --git a/src/Spring/Spring.Core/Objects/Factory/Config/SpecialFolderVariableSource.cs b/src/Spring/Spring.Core/Objects/Factory/Config/SpecialFolderVariableSource.cs
index 0048f39f..2ab74412 100644
--- a/src/Spring/Spring.Core/Objects/Factory/Config/SpecialFolderVariableSource.cs
+++ b/src/Spring/Spring.Core/Objects/Factory/Config/SpecialFolderVariableSource.cs
@@ -31,6 +31,17 @@ namespace Spring.Objects.Factory.Config
[Serializable]
public class SpecialFolderVariableSource : IVariableSource
{
+ ///
+ /// Before requesting a variable resolution, a client should
+ /// ask, whether the source can resolve a particular variable name.
+ ///
+ /// the name of the variable to resolve
+ /// true if the variable can be resolved, false otherwise
+ public bool CanResolveVariable(string name)
+ {
+ return ResolveVariable(name) != null;
+ }
+
///
/// Resolves specified special folder to its full path.
///
diff --git a/src/Spring/Spring.Core/Objects/Factory/Config/VariableAccessor.cs b/src/Spring/Spring.Core/Objects/Factory/Config/VariableAccessor.cs
index 2fa7158d..bacc5174 100644
--- a/src/Spring/Spring.Core/Objects/Factory/Config/VariableAccessor.cs
+++ b/src/Spring/Spring.Core/Objects/Factory/Config/VariableAccessor.cs
@@ -774,7 +774,11 @@ namespace Spring.Objects.Factory.Config
///
public string GetString(string name, string defaultValue)
{
- string value = (variableSource == null) ? defaultValue : variableSource.ResolveVariable(name);
+ string value = null;
+ if (variableSource != null && variableSource.CanResolveVariable(name))
+ {
+ value = variableSource.ResolveVariable(name);
+ }
if (!StringUtils.HasLength(value))
{
diff --git a/src/Spring/Spring.Core/Objects/Factory/Config/VariablePlaceholderConfigurer.cs b/src/Spring/Spring.Core/Objects/Factory/Config/VariablePlaceholderConfigurer.cs
index 7ac85308..a4e75065 100644
--- a/src/Spring/Spring.Core/Objects/Factory/Config/VariablePlaceholderConfigurer.cs
+++ b/src/Spring/Spring.Core/Objects/Factory/Config/VariablePlaceholderConfigurer.cs
@@ -24,6 +24,7 @@ using System.Globalization;
using Common.Logging;
using Spring.Collections;
using Spring.Core;
+using Spring.Util;
namespace Spring.Objects.Factory.Config
{
@@ -66,12 +67,12 @@ namespace Spring.Objects.Factory.Config
///
/// The default placeholder prefix.
///
- public const string DefaultPlaceholderPrefix = "${";
+ public static readonly string DefaultPlaceholderPrefix = "${";
///
/// The default placeholder suffix.
///
- public const string DefaultPlaceholderSuffix = "}";
+ public static readonly string DefaultPlaceholderSuffix = "}";
#region Fields
@@ -81,10 +82,33 @@ namespace Spring.Objects.Factory.Config
private string placeholderPrefix = DefaultPlaceholderPrefix;
private string placeholderSuffix = DefaultPlaceholderSuffix;
- private IList variableSourceList;
+ private IList variableSourceList = new ArrayList();
#endregion
+ ///
+ /// Create a new instance without any variable sources
+ ///
+ public VariablePlaceholderConfigurer()
+ {}
+
+ ///
+ /// Create a new instance and initialize with the given variable source
+ ///
+ ///
+ public VariablePlaceholderConfigurer(IVariableSource variableSource)
+ {
+ this.VariableSource = variableSource;
+ }
+
+ ///
+ /// Create a new instance and initialize with the given list of variable sources
+ ///
+ public VariablePlaceholderConfigurer(IList variableSources)
+ {
+ this.VariableSources = variableSources;
+ }
+
#region Properties
///
@@ -156,6 +180,17 @@ namespace Spring.Objects.Factory.Config
///
public void PostProcessObjectFactory( IConfigurableListableObjectFactory factory )
{
+ if (CollectionUtils.IsEmpty(variableSourceList))
+ {
+ throw new ArgumentException("No VariableSources configured");
+ }
+
+ ICollection filtered = CollectionUtils.FindValuesOfType(this.variableSourceList, typeof (IVariableSource));
+ if (filtered.Count != this.variableSourceList.Count)
+ {
+ throw new ArgumentException("'VariableSources' must contain IVariableSource elements only", "VariableSources");
+ }
+
try
{
ProcessProperties( factory );
@@ -206,11 +241,9 @@ namespace Spring.Objects.Factory.Config
///
protected virtual void ProcessProperties( IConfigurableListableObjectFactory factory )
{
- IVariableSource compositeVariableSource = new PlaceholderResolvingCompositeVariableSource( placeholderPrefix
- , placeholderSuffix
- , variableSourceList
- , ignoreUnresolvablePlaceholders );
- ObjectDefinitionVisitor visitor = new ObjectDefinitionVisitor( compositeVariableSource );
+ CompositeVariableSource compositeVariableSource = new CompositeVariableSource(variableSourceList);
+ TextProcessor tp = new TextProcessor(this, compositeVariableSource);
+ ObjectDefinitionVisitor visitor = new ObjectDefinitionVisitor(new ObjectDefinitionVisitor.ResolveHandler(tp.ParseAndResolveVariables));
string[] objectDefinitionNames = factory.GetObjectDefinitionNames();
for (int i = 0; i < objectDefinitionNames.Length; ++i)
@@ -231,84 +264,86 @@ namespace Spring.Objects.Factory.Config
#region Helper class
- private class PlaceholderResolvingCompositeVariableSource : IVariableSource
+ private class TextProcessor
{
- private readonly ILog logger = LogManager.GetLogger( typeof( PlaceholderResolvingCompositeVariableSource ) );
+ private readonly ILog logger = LogManager.GetLogger(typeof(TextProcessor));
+ private readonly VariablePlaceholderConfigurer owner;
+ private readonly IVariableSource variableSource;
- private readonly string placeholderPrefix;
- private readonly string placeholderSuffix;
- private readonly bool ignoreUnresolvablePlaceholders;
- private readonly IList variableSourceList;
-
- public PlaceholderResolvingCompositeVariableSource( string placeholderPrefix, string placeholderSuffix, IList variableSourceList, bool ignoreUnresolvablePlaceholders )
+ public TextProcessor(VariablePlaceholderConfigurer owner, IVariableSource variableSource)
{
- this.placeholderPrefix = placeholderPrefix;
- this.placeholderSuffix = placeholderSuffix;
- this.variableSourceList = variableSourceList;
- this.ignoreUnresolvablePlaceholders = ignoreUnresolvablePlaceholders;
+ this.owner = owner;
+ this.variableSource = variableSource;
}
- #region IVariableSource Members
-
- public string ResolveVariable( string rawStringValue )
+ public string ParseAndResolveVariables(string rawStringValue)
{
- return ParseAndResolveVariable( rawStringValue, new HashedSet() );
+ return ParseAndResolveVariables(rawStringValue, new HashedSet());
}
-
- //TODO handle resolved values at are not string - identify this case as only 1 placeholder present?
-
- private string ParseAndResolveVariable( string strVal, ISet visitedPlaceholders )
+ private string ParseAndResolveVariables(string strVal, ISet visitedPlaceholders)
{
- int startIndex = strVal.IndexOf( placeholderPrefix );
+ if (strVal == null)
+ {
+ return null;
+ }
+
+ int startIndex = strVal.IndexOf(owner.placeholderPrefix);
while (startIndex != -1)
{
int endIndex = strVal.IndexOf(
- placeholderSuffix, startIndex + placeholderPrefix.Length );
+ owner.placeholderSuffix, startIndex + owner.placeholderPrefix.Length);
if (endIndex != -1)
{
- int pos = startIndex + placeholderPrefix.Length;
- string placeholder = strVal.Substring( pos, endIndex - pos );
- if (visitedPlaceholders.Contains( placeholder ))
+ int pos = startIndex + owner.placeholderPrefix.Length;
+ string placeholder = strVal.Substring(pos, endIndex - pos);
+ if (visitedPlaceholders.Contains(placeholder))
{
throw new ObjectDefinitionStoreException(
string.Format(
CultureInfo.InvariantCulture,
"Circular placeholder reference '{0}' detected. ",
- placeholder ) );
+ placeholder));
}
- visitedPlaceholders.Add( placeholder );
- string resolvedValue = ResolvePlaceholderVariable( placeholder );
- if (resolvedValue != null)
+ visitedPlaceholders.Add(placeholder);
+
+ if (variableSource.CanResolveVariable(placeholder))
{
- resolvedValue = ParseAndResolveVariable( resolvedValue, visitedPlaceholders );
+ string resolvedValue = variableSource.ResolveVariable(placeholder);
+ resolvedValue = ParseAndResolveVariables(resolvedValue, visitedPlaceholders);
#region Instrumentation
if (logger.IsDebugEnabled)
{
- logger.Debug( string.Format(
+ logger.Debug(string.Format(
CultureInfo.InvariantCulture,
- "Resolving placeholder '{0}' to '{1}'.", placeholder, resolvedValue ) );
+ "Resolving placeholder '{0}' to '{1}'.", placeholder, resolvedValue));
}
#endregion
- strVal = strVal.Substring( 0, startIndex ) + resolvedValue + strVal.Substring( endIndex + placeholderSuffix.Length );
- startIndex = strVal.IndexOf( placeholderPrefix, startIndex + resolvedValue.Length);
+ if (resolvedValue == null
+ && startIndex == 0
+ && strVal.Length <= endIndex + owner.placeholderSuffix.Length)
+ {
+ return null;
+ }
+ strVal = strVal.Substring(0, startIndex) + resolvedValue + strVal.Substring(endIndex + owner.placeholderSuffix.Length);
+ startIndex = strVal.IndexOf(owner.placeholderPrefix, startIndex + (resolvedValue == null ? 0 : resolvedValue.Length));
}
- else if (ignoreUnresolvablePlaceholders)
+ else if (owner.ignoreUnresolvablePlaceholders)
{
// simply return the unprocessed value...
return strVal;
}
else
{
- throw new ObjectDefinitionStoreException( string.Format(
+ throw new ObjectDefinitionStoreException(string.Format(
CultureInfo.InvariantCulture,
- "Could not resolve placeholder '{0}'.", placeholder ) );
+ "Could not resolve placeholder '{0}'.", placeholder));
}
- visitedPlaceholders.Remove( placeholder );
+ visitedPlaceholders.Remove(placeholder);
}
else
{
@@ -316,34 +351,38 @@ namespace Spring.Objects.Factory.Config
}
}
return strVal;
+ }
+ }
+
+ private class CompositeVariableSource : IVariableSource
+ {
+ private readonly IList variableSourceList;
+
+ public CompositeVariableSource( IList variableSourceList )
+ {
+ this.variableSourceList = variableSourceList;
}
- private string ResolvePlaceholderVariable( string variableName )
+ public string ResolveVariable( string variableName )
{
foreach (IVariableSource variableSource in variableSourceList)
{
- //TODO handle resolved values at are not strings?
+ if (!variableSource.CanResolveVariable(variableName)) continue;
- object resolvedValue = variableSource.ResolveVariable( variableName );
- if (resolvedValue is string)
- {
- }
- if (resolvedValue != null)
- {
- if (resolvedValue is string)
- {
- return resolvedValue as string;
- }
- else
- {
- logger.Warn( "Placeholder " + variableSource + " resolved to object type [" + resolvedValue.GetType() + "]. Only string type currently supported" );
- }
- }
+ return variableSource.ResolveVariable( variableName );
}
- return null;
+ throw new ArgumentException(string.Format("cannot resolve variable '{0}'", variableName));
}
- #endregion
+ public bool CanResolveVariable(string variableName)
+ {
+ foreach (IVariableSource variableSource in variableSourceList)
+ {
+ if (variableSource.CanResolveVariable(variableName))
+ return true;
+ }
+ return false;
+ }
}
#endregion
diff --git a/src/Spring/Spring.Core/Spring.Core.2008.csproj b/src/Spring/Spring.Core/Spring.Core.2008.csproj
index a70fb52a..0a49fa7c 100644
--- a/src/Spring/Spring.Core/Spring.Core.2008.csproj
+++ b/src/Spring/Spring.Core/Spring.Core.2008.csproj
@@ -666,6 +666,7 @@
+
diff --git a/src/Spring/Spring.Core/Util/CollectionUtils.cs b/src/Spring/Spring.Core/Util/CollectionUtils.cs
index 10e064d2..94fb820e 100644
--- a/src/Spring/Spring.Core/Util/CollectionUtils.cs
+++ b/src/Spring/Spring.Core/Util/CollectionUtils.cs
@@ -70,20 +70,36 @@ namespace Spring.Util
/// The collection to check.
/// The object to locate in the collection.
/// if the element is in the collection, otherwise.
- public static bool Contains(ICollection collection, Object element)
+ public static bool Contains(IEnumerable 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", "Collection cannot be null.");
+ return false;
}
- MethodInfo method;
- method = collection.GetType().GetMethod("contains", BindingFlags.IgnoreCase | BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public);
- if (null == method)
+
+ if (collection is IList)
{
- throw new InvalidOperationException("Collection type " + collection.GetType() + " does not implement a Contains() method.");
+ return ((IList) collection).Contains(element);
}
- return (bool)method.Invoke(collection, new Object[] { element });
+
+ if (collection is IDictionary)
+ {
+ return ((IDictionary) collection).Contains(element);
+ }
+
+ MethodInfo method = collection.GetType().GetMethod("contains", BindingFlags.IgnoreCase | BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public);
+ if (null != method)
+ {
+ return (bool)method.Invoke(collection, new Object[] { element });
+ }
+ foreach (object item in collection)
+ {
+ if (object.Equals(item, element))
+ {
+ return true;
+ }
+ }
+ return false;
}
///
diff --git a/test/Spring/Spring.Core.Tests/Expressions/ExpressionEvaluatorTests.cs b/test/Spring/Spring.Core.Tests/Expressions/ExpressionEvaluatorTests.cs
index b0a0c73d..db8af40f 100644
--- a/test/Spring/Spring.Core.Tests/Expressions/ExpressionEvaluatorTests.cs
+++ b/test/Spring/Spring.Core.Tests/Expressions/ExpressionEvaluatorTests.cs
@@ -723,6 +723,18 @@ namespace Spring.Expressions
Assert.IsTrue((bool)ExpressionEvaluator.GetValue(tesla, "T(System.DateTime) == DOB.GetType()"));
}
+ ///
+ /// Tests type node
+ ///
+ [Test]
+ public void TestTypeNodeWithArrays()
+ {
+ Assert.AreEqual(typeof(DateTime[]), ExpressionEvaluator.GetValue(null, "T(System.DateTime[])"));
+ Assert.AreEqual(typeof(DateTime[,]), ExpressionEvaluator.GetValue(null, "T(System.DateTime[,])"));
+ Assert.AreEqual(typeof(DateTime[]), ExpressionEvaluator.GetValue(null, "T(System.DateTime[], mscorlib)"));
+ Assert.AreEqual(typeof(DateTime[,]), ExpressionEvaluator.GetValue(null, "T(System.DateTime[,], mscorlib)"));
+ }
+
///
/// Tests type node
///
diff --git a/test/Spring/Spring.Core.Tests/Objects/Factory/Config/ObjectDefinitionVisitorTests.cs b/test/Spring/Spring.Core.Tests/Objects/Factory/Config/ObjectDefinitionVisitorTests.cs
index 2e441657..34b8c7e8 100644
--- a/test/Spring/Spring.Core.Tests/Objects/Factory/Config/ObjectDefinitionVisitorTests.cs
+++ b/test/Spring/Spring.Core.Tests/Objects/Factory/Config/ObjectDefinitionVisitorTests.cs
@@ -18,10 +18,8 @@
#endregion
-using System;
using System.Collections;
using System.Collections.Specialized;
-
using NUnit.Framework;
using Spring.Objects.Factory.Support;
@@ -34,14 +32,22 @@ namespace Spring.Objects.Factory.Config
[TestFixture]
public class ObjectDefinitionVisitorTests
{
- private NameValueCollectionVariableSource variableSource;
+ private Hashtable properties;
[SetUp]
public void SetUp()
{
- NameValueCollection nvc = new NameValueCollection();
- nvc.Add("Property", "Value");
- variableSource = new NameValueCollectionVariableSource(nvc);
+ properties = CollectionsUtil.CreateCaseInsensitiveHashtable();
+ properties.Add("Property", "Value");
+ }
+
+ private string ParseAndResolveVariables(string rawText)
+ {
+ if (rawText.StartsWith("$"))
+ {
+ return (string) properties[rawText.Substring(1)];
+ }
+ return rawText;
}
[Test]
@@ -50,7 +56,7 @@ namespace Spring.Objects.Factory.Config
IObjectDefinition od = new RootObjectDefinition();
od.ObjectTypeName = "$Property";
- ObjectDefinitionVisitor odv = new ObjectDefinitionVisitor(variableSource);
+ ObjectDefinitionVisitor odv = new ObjectDefinitionVisitor(new ObjectDefinitionVisitor.ResolveHandler(ParseAndResolveVariables));
odv.VisitObjectDefinition(od);
Assert.AreEqual("Value", od.ObjectTypeName);
@@ -62,7 +68,7 @@ namespace Spring.Objects.Factory.Config
IObjectDefinition od = new RootObjectDefinition();
od.PropertyValues.Add("PropertyName", "$Property");
- ObjectDefinitionVisitor odv = new ObjectDefinitionVisitor(variableSource);
+ ObjectDefinitionVisitor odv = new ObjectDefinitionVisitor(new ObjectDefinitionVisitor.ResolveHandler(ParseAndResolveVariables));
odv.VisitObjectDefinition(od);
Assert.AreEqual("Value", od.PropertyValues.GetPropertyValue("PropertyName").Value);
@@ -78,7 +84,7 @@ namespace Spring.Objects.Factory.Config
ml.Add("$Property");
od.PropertyValues.Add("PropertyName", ml);
- ObjectDefinitionVisitor odv = new ObjectDefinitionVisitor(variableSource);
+ ObjectDefinitionVisitor odv = new ObjectDefinitionVisitor(new ObjectDefinitionVisitor.ResolveHandler(ParseAndResolveVariables));
odv.VisitObjectDefinition(od);
ManagedList list = od.PropertyValues.GetPropertyValue("PropertyName").Value as ManagedList;
@@ -96,7 +102,7 @@ namespace Spring.Objects.Factory.Config
ms.Add("$Property");
od.PropertyValues.Add("PropertyName", ms);
- ObjectDefinitionVisitor odv = new ObjectDefinitionVisitor(variableSource);
+ ObjectDefinitionVisitor odv = new ObjectDefinitionVisitor(new ObjectDefinitionVisitor.ResolveHandler(ParseAndResolveVariables));
odv.VisitObjectDefinition(od);
ManagedSet set = od.PropertyValues.GetPropertyValue("PropertyName").Value as ManagedSet;
@@ -117,7 +123,7 @@ namespace Spring.Objects.Factory.Config
md.Add("Key", "$Property");
od.PropertyValues.Add("PropertyName", md);
- ObjectDefinitionVisitor odv = new ObjectDefinitionVisitor(variableSource);
+ ObjectDefinitionVisitor odv = new ObjectDefinitionVisitor(new ObjectDefinitionVisitor.ResolveHandler(ParseAndResolveVariables));
odv.VisitObjectDefinition(od);
ManagedDictionary dictionary = od.PropertyValues.GetPropertyValue("PropertyName").Value as ManagedDictionary;
@@ -135,7 +141,7 @@ namespace Spring.Objects.Factory.Config
nvc["Key"] = "$Property";
od.PropertyValues.Add("PropertyName", nvc);
- ObjectDefinitionVisitor odv = new ObjectDefinitionVisitor(variableSource);
+ ObjectDefinitionVisitor odv = new ObjectDefinitionVisitor(new ObjectDefinitionVisitor.ResolveHandler(ParseAndResolveVariables));
odv.VisitObjectDefinition(od);
NameValueCollection visitedNvc =
@@ -143,31 +149,5 @@ namespace Spring.Objects.Factory.Config
Assert.AreEqual("Value", visitedNvc["Key"]);
}
-
- #region Helper class
-
- public class NameValueCollectionVariableSource : IVariableSource
- {
- private NameValueCollection properties;
-
- public NameValueCollectionVariableSource(NameValueCollection properties)
- {
- this.properties = properties;
- }
-
- public string ResolveVariable(string name)
- {
- if (name.StartsWith("$"))
- {
- return properties[name.Substring(1)];
- }
- else
- {
- return name;
- }
- }
- }
-
- #endregion
}
}
\ No newline at end of file
diff --git a/test/Spring/Spring.Core.Tests/Objects/Factory/Config/VariableAccessorTests.cs b/test/Spring/Spring.Core.Tests/Objects/Factory/Config/VariableAccessorTests.cs
index 92d7208f..31939fa9 100644
--- a/test/Spring/Spring.Core.Tests/Objects/Factory/Config/VariableAccessorTests.cs
+++ b/test/Spring/Spring.Core.Tests/Objects/Factory/Config/VariableAccessorTests.cs
@@ -41,23 +41,7 @@ namespace Spring.Objects.Factory.Config
private static readonly DateTime TESTDATETIME = new DateTime(2007, 07, 06, 11, 12, 13);
private static readonly DateTime TESTDATETIME_DEFAULT = TESTDATETIME.AddDays(-1);
- private class NameValueCollectionVariableSource : IVariableSource
- {
- private readonly NameValueCollection nameValues = new NameValueCollection();
-
- public NameValueCollectionVariableSource Add(string name, string value)
- {
- nameValues.Add(name, value);
- return this;
- }
-
- public string ResolveVariable(string name)
- {
- return nameValues.Get(name);
- }
- }
-
- private readonly IVariableSource _testVariableSource = new NameValueCollectionVariableSource()
+ private readonly IVariableSource _testVariableSource = new DictionaryVariableSource(null, true)
.Add("ValidString", "String")
.Add("EmptyString", "")
.Add("ValidChar", "c")
diff --git a/test/Spring/Spring.Core.Tests/Objects/Factory/Config/VariablePlaceholderConfigurerTests.cs b/test/Spring/Spring.Core.Tests/Objects/Factory/Config/VariablePlaceholderConfigurerTests.cs
index cb334326..e453cfd5 100644
--- a/test/Spring/Spring.Core.Tests/Objects/Factory/Config/VariablePlaceholderConfigurerTests.cs
+++ b/test/Spring/Spring.Core.Tests/Objects/Factory/Config/VariablePlaceholderConfigurerTests.cs
@@ -1,7 +1,7 @@
#region License
/*
- * Copyright © 2002-2007 the original author or authors.
+ * 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.
@@ -18,45 +18,49 @@
#endregion
-#region Imports
-
using System;
using System.Collections;
using NUnit.Framework;
using Spring.Context.Support;
-
-#endregion
+using Spring.Objects.Factory.Support;
namespace Spring.Objects.Factory.Config
{
///
- /// This calss contains tests for
+ /// This class contains tests for
///
/// Mark Pollack
[TestFixture]
public class VariablePlaceholderConfigurerTests
{
- private class DictionaryVariableSource : IVariableSource
+ [Test]
+ public void ThrowsOnMissingVariableSources()
{
- private Hashtable variables = new Hashtable();
-
- public DictionaryVariableSource(params string[] args)
+ StaticApplicationContext ac = new StaticApplicationContext();
+ VariablePlaceholderConfigurer vphc = new VariablePlaceholderConfigurer();
+ try
{
- for(int i=0;i