diff --git a/src/Spring/Spring.Template.Velocity/Template/Velocity/Config/TemplateNamespaceParser.cs b/src/Spring/Spring.Template.Velocity/Template/Velocity/Config/TemplateNamespaceParser.cs
index c2003389..510fac7b 100644
--- a/src/Spring/Spring.Template.Velocity/Template/Velocity/Config/TemplateNamespaceParser.cs
+++ b/src/Spring/Spring.Template.Velocity/Template/Velocity/Config/TemplateNamespaceParser.cs
@@ -28,6 +28,7 @@ using NVelocity.Runtime;
using NVelocity.Runtime.Resource.Loader;
using Spring.Core.TypeResolution;
using Spring.Objects.Factory;
+using Spring.Objects.Factory.Config;
using Spring.Objects.Factory.Support;
using Spring.Objects.Factory.Xml;
using Spring.Template.Velocity;
@@ -273,7 +274,8 @@ namespace Spring.Template.Velocity.Config {
/// the parser context
/// the properties used to initialize the velocity engine
private void ParseNVelocityProperties(XmlElement element, ParserContext parserContext, IDictionary properties) {
- IDictionary parsedProperties = objectNamespaceParserHelper.ParseDictionaryElementInternal(element, "NVelocityProperties", parserContext);
+ IDictionary parsedProperties = objectNamespaceParserHelper.ParseDictionaryElementInternal(element,
+ TemplateDefinitionConstants.ElementNVelocityProperties, parserContext);
foreach (DictionaryEntry entry in parsedProperties) {
properties.Add(Convert.ToString(entry.Key), entry.Value);
}
@@ -287,17 +289,6 @@ namespace Spring.Template.Velocity.Config {
return TemplateTypePrefix + element.LocalName;
}
-
- ///
- /// Helper class to access the ParseDictionaryElement of the ObjectsNamespaceParser
- /// todo: remove this and externalize the logic in ObjectsNamespaceParser.ParseDictionaryElement
- ///
- internal sealed class ObjectNamespaceParserHelper : ObjectsNamespaceParser {
- public IDictionary ParseDictionaryElementInternal(XmlElement element, string name, ParserContext parserContext) {
- return ParseDictionaryElement(element, name, parserContext);
- }
- }
-
///
/// constructs an nvelocity style resource loader property in the format:
/// prefix.resource.loader.suffix
@@ -309,7 +300,139 @@ namespace Spring.Template.Velocity.Config {
return type + VelocityConstants.Separator + RuntimeConstants.RESOURCE_LOADER + VelocityConstants.Separator +
suffix;
}
- #region Element & Attribute Name Constants
+ }
+
+
+
+ ///
+ /// Helper class to access the ParseDictionaryElement of the ObjectsNamespaceParser
+ /// todo: remove this and externalize the logic in ObjectsNamespaceParser.ParseDictionaryElement
+ ///
+ internal class ObjectNamespaceParserHelper : ObjectsNamespaceParser {
+ ///
+ /// Gets a dictionary definition.
+ ///
+ /// The element describing the dictionary definition.
+ /// The name of the object (definition) associated with the dictionary definition.
+ /// The namespace-aware parser.
+ /// The dictionary definition.
+ public IDictionary ParseDictionaryElementInternal(XmlElement mapEle, string name, ParserContext parserContext) {
+ ManagedDictionary dictionary = new ManagedDictionary();
+ string keyTypeName = GetAttributeValue(mapEle, "key-type");
+ string valueTypeName = GetAttributeValue(mapEle, "value-type");
+ if (StringUtils.HasText(keyTypeName)) {
+ dictionary.KeyTypeName = keyTypeName;
+ }
+ if (StringUtils.HasText(valueTypeName)) {
+ dictionary.ValueTypeName = valueTypeName;
+ }
+ dictionary.MergeEnabled = ParseMergeAttribute(mapEle, parserContext.ParserHelper);
+
+ XmlNodeList entryElements = SelectNodes(mapEle, ObjectDefinitionConstants.EntryElement);
+ foreach (XmlElement entryEle in entryElements) {
+ #region Key
+
+ object key = null;
+
+ XmlAttribute keyAtt = entryEle.Attributes[ObjectDefinitionConstants.KeyAttribute];
+ if (keyAtt != null) {
+ key = keyAtt.Value;
+ } else {
+ // ok, we're not using the 'key' attribute; lets check for the ref shortcut...
+ XmlAttribute keyRefAtt = entryEle.Attributes[ObjectDefinitionConstants.DictionaryKeyRefShortcutAttribute];
+ if (keyRefAtt != null) {
+ key = new RuntimeObjectReference(keyRefAtt.Value);
+ } else {
+ // so check for the 'key' element...
+ XmlNode keyNode = SelectSingleNode(entryEle, ObjectDefinitionConstants.KeyElement);
+ if (keyNode == null) {
+ throw new ObjectDefinitionStoreException(
+ parserContext.ReaderContext.Resource, name,
+ string.Format("One of either the '{0}' element, or the the '{1}' or '{2}' attributes " +
+ "is required for the <{3}/> element.",
+ ObjectDefinitionConstants.KeyElement,
+ ObjectDefinitionConstants.KeyAttribute,
+ ObjectDefinitionConstants.DictionaryKeyRefShortcutAttribute,
+ ObjectDefinitionConstants.EntryElement));
+ }
+ XmlElement keyElement = (XmlElement)keyNode;
+ XmlNodeList keyNodes = keyElement.GetElementsByTagName("*");
+ if (keyNodes == null || keyNodes.Count == 0) {
+ throw new ObjectDefinitionStoreException(
+ parserContext.ReaderContext.Resource, name,
+ string.Format("Malformed <{0}/> element... the value of the key must be " +
+ "specified as a child value-style element.",
+ ObjectDefinitionConstants.KeyElement));
+ }
+ key = ParsePropertySubElement((XmlElement)keyNodes.Item(0), name, parserContext);
+ }
+ }
+
+ #endregion
+
+ #region Value
+
+ XmlAttribute inlineValueAtt = entryEle.Attributes[ObjectDefinitionConstants.ValueAttribute];
+ if (inlineValueAtt != null) {
+ // ok, we're using the value attribute shortcut...
+ dictionary[key] = inlineValueAtt.Value;
+ } else if (entryEle.Attributes[ObjectDefinitionConstants.DictionaryValueRefShortcutAttribute] != null) {
+ // ok, we're using the value-ref attribute shortcut...
+ XmlAttribute inlineValueRefAtt = entryEle.Attributes[ObjectDefinitionConstants.DictionaryValueRefShortcutAttribute];
+ RuntimeObjectReference ror = new RuntimeObjectReference(inlineValueRefAtt.Value);
+ dictionary[key] = ror;
+ } else if (entryEle.Attributes[ObjectDefinitionConstants.ExpressionAttribute] != null) {
+ // ok, we're using the expression attribute shortcut...
+ XmlAttribute inlineExpressionAtt = entryEle.Attributes[ObjectDefinitionConstants.ExpressionAttribute];
+ ExpressionHolder expHolder = new ExpressionHolder(inlineExpressionAtt.Value);
+ dictionary[key] = expHolder;
+ } else {
+ XmlNode keyNode = SelectSingleNode(entryEle, ObjectDefinitionConstants.KeyElement);
+ if (keyNode != null) {
+ entryEle.RemoveChild(keyNode);
+ }
+ // ok, we're using the original full-on value element...
+ XmlNodeList valueElements = entryEle.GetElementsByTagName("*");
+ if (valueElements == null || valueElements.Count == 0) {
+ throw new ObjectDefinitionStoreException(
+ parserContext.ReaderContext.Resource, name,
+ string.Format("One of either the '{0}' or '{1}' attributes, or a value-style element " +
+ "is required for the <{2}/> element.",
+ ObjectDefinitionConstants.ValueAttribute, ObjectDefinitionConstants.DictionaryValueRefShortcutAttribute, ObjectDefinitionConstants.EntryElement));
+ }
+ dictionary[key] = ParsePropertySubElement((XmlElement)valueElements.Item(0), name, parserContext);
+ }
+
+ #endregion
+ }
+ return dictionary;
+ }
+ private bool ParseMergeAttribute(XmlElement collectionElement, ObjectDefinitionParserHelper helper) {
+ string val = collectionElement.GetAttribute(ObjectDefinitionConstants.MergeAttribute);
+ if (ObjectDefinitionConstants.DefaultValue.Equals(val)) {
+ val = helper.Defaults.Merge;
+ }
+ return ObjectDefinitionConstants.TrueValue.Equals(val);
+ }
+
+ ///
+ /// This method overrides SelectNodes from ObjectsNamespaceParser because the original method
+ /// picks up the NamespaceURI from the nvelocity which causes element.SelectNodes to return an empty
+ /// list. Consider removing.
+ ///
+ protected new XmlNodeList SelectNodes(XmlElement element, string childElementName) {
+ XmlNamespaceManager nsManager = new XmlNamespaceManager(new NameTable());
+ nsManager.AddNamespace(GetNamespacePrefix(element), Namespace);
+ return element.SelectNodes(GetNamespacePrefix(element) + ":" + childElementName, nsManager);
+ }
+
+
+ private string GetNamespacePrefix(XmlElement element) {
+ return StringUtils.HasText(element.Prefix) ? element.Prefix : "spring";
+ }
+ }
+
+ #region Element & Attribute Name Constants
///
/// Template definition constants
@@ -426,5 +549,4 @@ namespace Spring.Template.Velocity.Config {
public const string SpringResourceLoaderClass = "Spring.Template.Velocity.SpringResourceLoader; Spring.Template.Velocity";
}
#endregion
- }
}
\ No newline at end of file
diff --git a/test/Spring/Spring.Template.Velocity.Tests/Template/Velocity/Config/TemplateNamespaceParserTests.cs b/test/Spring/Spring.Template.Velocity.Tests/Template/Velocity/Config/TemplateNamespaceParserTests.cs
index 99f142a1..0452dda6 100644
--- a/test/Spring/Spring.Template.Velocity.Tests/Template/Velocity/Config/TemplateNamespaceParserTests.cs
+++ b/test/Spring/Spring.Template.Velocity.Tests/Template/Velocity/Config/TemplateNamespaceParserTests.cs
@@ -22,6 +22,7 @@
using System.Collections;
using System.IO;
+using System.Text;
using Commons.Collections;
using NUnit.Framework;
using NVelocity.App;
@@ -42,9 +43,9 @@ namespace Spring.Template.Velocity.Tests.Template.Velocity.Config {
public class TemplateNamespaceParserTests : VelocityEngineTestBase {
#region convinience properties aliases
private const string PropertyModificationCheck =
- TemplateNamespaceParser.TemplateDefinitionConstants.PropertyResourceLoaderModificationCheckInterval;
+ TemplateDefinitionConstants.PropertyResourceLoaderModificationCheckInterval;
private const string PropertyResourceLoaderCachce =
- TemplateNamespaceParser.TemplateDefinitionConstants.PropertyResourceLoaderCaching;
+ TemplateDefinitionConstants.PropertyResourceLoaderCaching;
#endregion
#region test constants
@@ -61,7 +62,7 @@ namespace Spring.Template.Velocity.Tests.Template.Velocity.Config {
VelocityEngine velocityEngine = appContext.GetObject("cnFileVelocityEngine") as VelocityEngine;
Assert.IsNotNull(velocityEngine, "velocity engine is null");
Assert.AreEqual(VelocityConstants.File, getSingleProperty(velocityEngine, RuntimeConstants.RESOURCE_LOADER), "incorrect resource loader");
- Assert.AreEqual(TemplateNamespaceParser.TemplateDefinitionConstants.FileResourceLoaderClass, getSingleProperty(velocityEngine,
+ Assert.AreEqual(TemplateDefinitionConstants.FileResourceLoaderClass, getSingleProperty(velocityEngine,
TemplateNamespaceParser.getResourceLoaderProperty(VelocityConstants.File, VelocityConstants.Class)), "incorrect resource loader type");
Assert.AreEqual(new string[]{"Template/Velocity/", "Template/"}, velocityEngine.GetProperty(
TemplateNamespaceParser.getResourceLoaderProperty(VelocityConstants.File, VelocityConstants.Path)), "incorrect resource loader path");
@@ -82,7 +83,7 @@ namespace Spring.Template.Velocity.Tests.Template.Velocity.Config {
VelocityEngine velocityEngine = appContext.GetObject("cnAssemblyVelocityEngine") as VelocityEngine;
Assert.IsNotNull(velocityEngine, "velocity engine is null");
Assert.AreEqual(VelocityConstants.Assembly, getSingleProperty(velocityEngine, RuntimeConstants.RESOURCE_LOADER), "incorrect resource loader");
- Assert.AreEqual(TemplateNamespaceParser.TemplateDefinitionConstants.AssemblyResourceLoaderClass, getSingleProperty(velocityEngine,
+ Assert.AreEqual(TemplateDefinitionConstants.AssemblyResourceLoaderClass, getSingleProperty(velocityEngine,
TemplateNamespaceParser.getResourceLoaderProperty(VelocityConstants.Assembly, VelocityConstants.Class)), "incorrect resource loader type");
Assert.AreEqual("Spring.Template.Velocity.Tests", getSingleProperty(velocityEngine,
TemplateNamespaceParser.getResourceLoaderProperty(VelocityConstants.Assembly,VelocityConstants.Assembly)), "incorrect resource loader path");
@@ -99,9 +100,9 @@ namespace Spring.Template.Velocity.Tests.Template.Velocity.Config {
public void TestSpringBasedConfig() {
VelocityEngine velocityEngine = appContext.GetObject("cnSpringVelocityEngine") as VelocityEngine;
Assert.IsNotNull(velocityEngine, "velocity engine is null");
- const string PropertySpring = TemplateNamespaceParser.TemplateDefinitionConstants.Spring;
+ const string PropertySpring = TemplateDefinitionConstants.Spring;
Assert.AreEqual(PropertySpring, getSingleProperty(velocityEngine, RuntimeConstants.RESOURCE_LOADER), "incorrect resource loader");
- Assert.AreEqual(TemplateNamespaceParser.TemplateDefinitionConstants.SpringResourceLoaderClass, getSingleProperty(velocityEngine,
+ Assert.AreEqual(TemplateDefinitionConstants.SpringResourceLoaderClass, getSingleProperty(velocityEngine,
TemplateNamespaceParser.getResourceLoaderProperty(PropertySpring, VelocityConstants.Class)), "incorrect resource loader type");
// no way to test the path property other than trying to actually perform a merge (it is set in velocity engine as an application attribute which is not exposed)
Assert.AreEqual(DEFAULT_CACHE_SIZE, velocityEngine.GetProperty(RuntimeConstants.RESOURCE_MANAGER_DEFAULTCACHE_SIZE), "incorrect default cache size");
@@ -129,6 +130,15 @@ namespace Spring.Template.Velocity.Tests.Template.Velocity.Config {
Assert.AreEqual("TestDescription", getSingleProperty(velocityEngine, descProp), "incorrect description");
}
+ [Test]
+ public void TestLocalConfig() {
+ VelocityEngine velocityEngine = appContext.GetObject("cnVelocityEngineLocalConfig") as VelocityEngine;
+ Assert.IsNotNull(velocityEngine, "velocity engine is null");
+
+ Assert.AreEqual(Encoding.UTF8.WebName.ToUpper(), getSingleProperty(velocityEngine, RuntimeConstants.INPUT_ENCODING), "incorrect input encoding value");
+ Assert.AreEqual(TEST_VALUE, getSingleProperty(velocityEngine, "myproperty.mysubproperty"), "incorrect custom property value");
+ }
+
///
/// Test a custom resource loader definition
///
diff --git a/test/Spring/Spring.Template.Velocity.Tests/Template/Velocity/VelocityEngineFactoryObjectTests.xml b/test/Spring/Spring.Template.Velocity.Tests/Template/Velocity/VelocityEngineFactoryObjectTests.xml
index 567bb04d..ad7bb506 100644
--- a/test/Spring/Spring.Template.Velocity.Tests/Template/Velocity/VelocityEngineFactoryObjectTests.xml
+++ b/test/Spring/Spring.Template.Velocity.Tests/Template/Velocity/VelocityEngineFactoryObjectTests.xml
@@ -20,18 +20,25 @@
-
+
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+