From ab9b98ec1597692ab71b1fe4194dafb7ca0ade5b Mon Sep 17 00:00:00 2001 From: hammett Date: Thu, 6 Dec 2007 12:15:22 +0000 Subject: [PATCH] - Applied James Curran's patch fixing MR-347 "Cannot specify a default value for ViewComponentParameterAttribute" git-svn-id: https://svn.castleproject.org/svn/castle/trunk@4560 73e77b4c-caa6-f847-a29a-24ab75ae54b6 --- .../Attributes/ViewComponentDetailsAttribute.cs | 72 ++++++++++++------- .../Attributes/ViewComponentParamAttribute.cs | 58 +++++++++------- .../Castle.MonoRail.Framework/ViewComponent.cs | 80 ++++++++++++---------- MonoRail/Changes.txt | 3 + 4 files changed, 127 insertions(+), 86 deletions(-) diff --git a/MonoRail/Castle.MonoRail.Framework/Attributes/ViewComponentDetailsAttribute.cs b/MonoRail/Castle.MonoRail.Framework/Attributes/ViewComponentDetailsAttribute.cs index 58609770e..41606c7ee 100644 --- a/MonoRail/Castle.MonoRail.Framework/Attributes/ViewComponentDetailsAttribute.cs +++ b/MonoRail/Castle.MonoRail.Framework/Attributes/ViewComponentDetailsAttribute.cs @@ -38,28 +38,28 @@ namespace Castle.MonoRail.Framework /// /// Decorates a to associate a custom name with it. /// - /// - /// Decorates a to associate a custom name with it. - /// - /// Optionally you can associate the section names supported by the - /// . - /// - /// - /// - /// In the code below, the class MyHeaderViewConponent will be referenced as just Header, - /// and it will support the subsections header and footer. - /// - /// - /// - /// - /// + /// + /// Decorates a to associate a custom name with it. + /// + /// Optionally you can associate the section names supported by the + /// . + /// + /// + /// + /// In the code below, the class MyHeaderViewConponent will be referenced as just Header, + /// and it will support the subsections header and footer. + /// + /// + /// + /// + /// [AttributeUsage(AttributeTargets.Class), Serializable] public class ViewComponentDetailsAttribute : Attribute { @@ -67,6 +67,7 @@ namespace Castle.MonoRail.Framework private string sections; private ViewComponentCache cache = ViewComponentCache.Disabled; private Type cacheKeyFactory; + private string[] sectionsFromAttribute; /// /// Initializes a new instance of the class. @@ -92,7 +93,19 @@ namespace Castle.MonoRail.Framework public string Sections { get { return sections; } - set { sections = value; } + set + { + sections = value; + if (!string.IsNullOrEmpty(sections)) + { + sectionsFromAttribute = sections.Split(new char[] {',', ' ', '|'}, StringSplitOptions.RemoveEmptyEntries); + } + + if (sectionsFromAttribute == null) + { + sectionsFromAttribute = new string[0]; + } + } } /// @@ -121,5 +134,16 @@ namespace Castle.MonoRail.Framework cacheKeyFactory = value; } } + + /// + /// Returns true if the section name specified is present on the list of sections. + /// + /// The section name. + /// + public bool SupportsSection(string name) + { + return Array.FindIndex(sectionsFromAttribute, + delegate(string item) { return string.Equals(item, name, StringComparison.InvariantCultureIgnoreCase); }) != -1; + } } -} +} \ No newline at end of file diff --git a/MonoRail/Castle.MonoRail.Framework/Attributes/ViewComponentParamAttribute.cs b/MonoRail/Castle.MonoRail.Framework/Attributes/ViewComponentParamAttribute.cs index cfca18083..962c03515 100644 --- a/MonoRail/Castle.MonoRail.Framework/Attributes/ViewComponentParamAttribute.cs +++ b/MonoRail/Castle.MonoRail.Framework/Attributes/ViewComponentParamAttribute.cs @@ -20,39 +20,39 @@ namespace Castle.MonoRail.Framework /// Decorates a public property in a /// to have the framework automatically bind the value using /// the dictionary. - /// - /// + /// + /// /// By default the property name is going to be used as a key to query the params. /// /// You can also use the /// property to define that a parameter is non-optional. /// - /// - /// - /// In the code below, the Text parameter will automatically be bound to the header property. - /// If there is no Text parameter, a will be thrown. - /// Simailrly, the optional CssClass parameter will be bound to the CssClass property. No error - /// occurs if there is no CssClass parameter. - /// - /// - /// - + /// + /// + /// In the code below, the Text parameter will automatically be bound to the header property. + /// If there is no Text parameter, a will be thrown. + /// Simailrly, the optional CssClass parameter will be bound to the CssClass property. No error + /// occurs if there is no CssClass parameter. + /// + /// + /// [AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = true), Serializable] public class ViewComponentParamAttribute : Attribute { private string paramName; private bool required; + private object defaultValue; /// /// Initializes a new instance of the class. @@ -90,5 +90,15 @@ namespace Castle.MonoRail.Framework { get { return paramName; } } + + /// + /// Gets or sets the default value for the parameter. + /// + /// The default. + public object Default + { + get { return defaultValue; } + set { defaultValue = value; } + } } -} +} \ No newline at end of file diff --git a/MonoRail/Castle.MonoRail.Framework/ViewComponent.cs b/MonoRail/Castle.MonoRail.Framework/ViewComponent.cs index 57102a47a..43bceee96 100644 --- a/MonoRail/Castle.MonoRail.Framework/ViewComponent.cs +++ b/MonoRail/Castle.MonoRail.Framework/ViewComponent.cs @@ -81,7 +81,23 @@ namespace Castle.MonoRail.Framework /// private IRailsEngineContext railsContext; - private string[] sectionsFromAttribute; + /// + /// Holds a reference to the if any. + /// + private ViewComponentDetailsAttribute detailsAtt; + + /// + /// Initializes a new instance of the ViewComponent class. + /// + public ViewComponent() + { + object[] attributes = GetType().GetCustomAttributes(typeof (ViewComponentDetailsAttribute), true); + + if (attributes.Length != 0) + { + detailsAtt = attributes[0] as ViewComponentDetailsAttribute; + } + } #region "Internal" core methods @@ -108,13 +124,13 @@ namespace Castle.MonoRail.Framework IConverter converter = new DefaultConverter(); PropertyInfo[] properties = GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance); - - foreach(PropertyInfo property in properties) + + foreach (PropertyInfo property in properties) { if (!property.CanWrite) continue; - object[] attributes = property.GetCustomAttributes(typeof(ViewComponentParamAttribute), true); - + object[] attributes = property.GetCustomAttributes(typeof (ViewComponentParamAttribute), true); + if (attributes.Length == 1) { BindParameter((ViewComponentParamAttribute) attributes[0], property, converter); @@ -126,15 +142,16 @@ namespace Castle.MonoRail.Framework { string compParamKey = string.IsNullOrEmpty(paramAtt.ParamName) ? property.Name : paramAtt.ParamName; - object value = ComponentParams[compParamKey]; + object value = ComponentParams[compParamKey] ?? paramAtt.Default; if (value == null) { - if (paramAtt.Required && - (property.PropertyType.IsValueType || property.GetValue(this, null) == null)) + if (paramAtt.Required && + (property.PropertyType.IsValueType || property.GetValue(this, null) == null)) { throw new ViewComponentException(string.Format("The parameter '{0}' is required by " + - "the ViewComponent {1} but was not passed or had a null value", compParamKey, GetType().Name)); + "the ViewComponent {1} but was not passed or had a null value", + compParamKey, GetType().Name)); } } else @@ -154,10 +171,11 @@ namespace Castle.MonoRail.Framework throw new Exception("Could not convert '" + value + "' to type " + property.PropertyType); } } - catch(Exception ex) + catch (Exception ex) { throw new ViewComponentException(string.Format("Error trying to set value for parameter '{0}' " + - "on ViewComponent {1}: {2}", compParamKey, GetType().Name, ex.Message), ex); + "on ViewComponent {1}: {2}", compParamKey, GetType().Name, + ex.Message), ex); } } } @@ -188,40 +206,26 @@ namespace Castle.MonoRail.Framework /// name is a known section the view component /// supports. /// + /// In general, this should not be implemented in a derived class. + /// should be used to indicate allowed section names instead. + /// /// section being added /// if section is supported public virtual bool SupportsSection(string name) { - // TODO: We need to cache this - - if (sectionsFromAttribute == null) + if (detailsAtt != null) { - object[] attributes = GetType().GetCustomAttributes(typeof(ViewComponentDetailsAttribute), true); - - if (attributes.Length != 0) - { - ViewComponentDetailsAttribute detailsAtt = (ViewComponentDetailsAttribute) attributes[0]; - - if (!string.IsNullOrEmpty(detailsAtt.Sections)) - { - sectionsFromAttribute = detailsAtt.Sections.Split(','); - } - } - - if (sectionsFromAttribute == null) - { - sectionsFromAttribute = new string[0]; - } + return detailsAtt.SupportsSection(name); + } + else + { + return false; } - - return Array.Find(sectionsFromAttribute, - delegate(string item) - { return string.Equals(item, name, StringComparison.InvariantCultureIgnoreCase); }) != null; } #endregion - #region Usefull properties + #region Useful properties /// /// Gets the Component Context @@ -415,13 +419,13 @@ namespace Castle.MonoRail.Framework private String GetBaseViewPath(String componentName, string name) { string viewPath = Path.Combine("components", componentName); - if(Context.ViewEngine.HasTemplate(Path.Combine(viewPath, name))==false) + if (Context.ViewEngine.HasTemplate(Path.Combine(viewPath, name)) == false) { - viewPath = Path.Combine("components", componentName+"Component"); + viewPath = Path.Combine("components", componentName + "Component"); } return viewPath; } #endregion } -} +} \ No newline at end of file diff --git a/MonoRail/Changes.txt b/MonoRail/Changes.txt index 6c33cdd87..f7f1069c3 100644 --- a/MonoRail/Changes.txt +++ b/MonoRail/Changes.txt @@ -1,6 +1,9 @@ 1.0 === +- Applied James Curran's patch fixing MR-347 + "Cannot specify a default value for ViewComponentParameterAttribute" + - Applied Adam Tybor's patch fixing MR-370 "RouteContext and Http Method Filtering for PatternRule" -- 2.11.4.GIT