diff --git a/src/Spring/Spring.Aop/Aop/Support/AbstractRegularExpressionMethodPointcut.cs b/src/Spring/Spring.Aop/Aop/Support/AbstractRegularExpressionMethodPointcut.cs index 6abb8a1e..ccb00252 100644 --- a/src/Spring/Spring.Aop/Aop/Support/AbstractRegularExpressionMethodPointcut.cs +++ b/src/Spring/Spring.Aop/Aop/Support/AbstractRegularExpressionMethodPointcut.cs @@ -1,5 +1,5 @@ -#region License - +#region License + /* * Copyright © 2002-2005 the original author or authors. * @@ -14,97 +14,97 @@ * 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 - -#region Imports - -using System; -using System.Reflection; -using System.Runtime.Serialization; -using System.Security.Permissions; -using AopAlliance.Aop; -using Spring.Core; -using Spring.Objects; -using Spring.Util; - -#endregion - -namespace Spring.Aop.Support -{ - /// - /// Abstract base regular expression pointcut object. - /// - /// - ///

- /// The regular expressions must be a match. For example, the - /// .*Get.* pattern will match Com.Mycom.Foo.GetBar(), and - /// Get.* will not. - ///

- ///

- /// This base class is serializable. Subclasses should decorate all - /// fields with the - the - /// - /// method in this class will be invoked again on the client side on deserialization. - ///

- ///
- /// Rod Johnson - /// Juergen Hoeller - /// Simon White (.NET) - [Serializable] - public abstract class AbstractRegularExpressionMethodPointcut - : StaticMethodMatcherPointcut, ITypeFilter, ISerializable - { - [NonSerialized] - private object[] _patterns = ObjectUtils.EmptyObjects; - - #region Constructors - - /// - /// Creates a new instance of the - /// - /// class. - /// - /// - ///

- /// This is an abstract class, and as such has no publicly - /// visible constructors. - ///

- ///
- protected AbstractRegularExpressionMethodPointcut() - { - } - - /// - /// Creates a new instance of the - /// class. - /// - /// - /// The - /// that holds the serialized object data about the exception being thrown. - /// - /// - /// The - /// that contains contextual information about the source or destination. - /// - /// - /// If an error was encountered during the deserialization process. - /// - protected AbstractRegularExpressionMethodPointcut( - SerializationInfo info, StreamingContext context) - { - _patterns = (object[]) info.GetValue("Patterns", typeof(object[])); + */ + +#endregion + +#region Imports + +using System; +using System.Reflection; +using System.Runtime.Serialization; +using System.Security.Permissions; +using AopAlliance.Aop; +using Spring.Core; +using Spring.Objects; +using Spring.Util; + +#endregion + +namespace Spring.Aop.Support +{ + /// + /// Abstract base regular expression pointcut object. + /// + /// + ///

+ /// The regular expressions must be a match. For example, the + /// .*Get.* pattern will match Com.Mycom.Foo.GetBar(), and + /// Get.* will not. + ///

+ ///

+ /// This base class is serializable. Subclasses should decorate all + /// fields with the - the + /// + /// method in this class will be invoked again on the client side on deserialization. + ///

+ ///
+ /// Rod Johnson + /// Juergen Hoeller + /// Simon White (.NET) + [Serializable] + public abstract class AbstractRegularExpressionMethodPointcut + : StaticMethodMatcherPointcut, ITypeFilter, ISerializable + { + [NonSerialized] + private object[] _patterns = ObjectUtils.EmptyObjects; + + #region Constructors + + /// + /// Creates a new instance of the + /// + /// class. + /// + /// + ///

+ /// This is an abstract class, and as such has no publicly + /// visible constructors. + ///

+ ///
+ protected AbstractRegularExpressionMethodPointcut() + { + } + + /// + /// Creates a new instance of the + /// class. + /// + /// + /// The + /// that holds the serialized object data about the exception being thrown. + /// + /// + /// The + /// that contains contextual information about the source or destination. + /// + /// + /// If an error was encountered during the deserialization process. + /// + protected AbstractRegularExpressionMethodPointcut( + SerializationInfo info, StreamingContext context) + { + _patterns = (object[])info.GetValue("Patterns", typeof(object[])); if (_patterns == null) { _patterns = ObjectUtils.EmptyObjects; } } - /// - /// Overridden to ensure proper initialization - /// - protected override void OnDeserialization(object sender) + /// + /// Overridden to ensure proper initialization + /// + protected override void OnDeserialization(object sender) { base.OnDeserialization(sender); try @@ -116,172 +116,184 @@ namespace Spring.Aop.Support throw new AspectException( "Failed to deserialize AOP regular expression pointcut: " + ex.Message); } - } - - #endregion - - #region Properties - - /// - /// The for this pointcut. - /// - /// - /// The current . - /// - public override ITypeFilter TypeFilter - { - get { return this; } - } - - /// - /// Convenience property for setting a single pattern. - /// - /// - /// Use this property or Patterns, not both. - /// - public virtual object Pattern - { - get { return (_patterns.Length > 0 ? _patterns[0] : null); } - set - { - AssertUtils.ArgumentNotNull(value, "Pattern"); - this.Patterns = new object[] {value}; - } - } - - /// - /// The regular expressions defining methods to match. - /// - /// - /// Matching will be the union of all these; if any match, - /// the pointcut matches. - /// - public virtual object[] Patterns - { - get { return _patterns; } - set - { - AssertUtils.ArgumentNotNull(value, "Patterns"); - this._patterns = value; - InitPatternRepresentation(this.Patterns); - } - } - - #endregion - - #region Methods - - /// - /// Populates a with - /// the data needed to serialize the target object. - /// - /// - /// The to populate - /// with data. - /// - /// - /// The destination (see ) - /// for this serialization. - /// - [SecurityPermission(SecurityAction.Demand, SerializationFormatter=true)] - public void GetObjectData(SerializationInfo info, StreamingContext context) - { - info.AddValue("Patterns", _patterns); - } - - /// - /// Subclasses must implement this to initialize regular expression pointcuts. - /// - /// - ///

- /// Can be invoked multiple times. - ///

- ///

- /// This method will be invoked from the property, - /// and also on deserialization. - ///

- ///
- /// - /// The patterns to initialize. - /// - /// - /// In the case of an invalid pattern. - /// - protected abstract void InitPatternRepresentation(object[] patterns); - - /// - /// Does the pattern at the supplied - /// match this ? - /// - /// The pattern to match - /// The index of pattern. - /// - /// if there is a match. - /// - protected abstract bool Matches(string pattern, int patternIndex); - - /// - /// Does the supplied satisfy this matcher? - /// - /// - ///

- /// Try to match the regular expression against the fully qualified name - /// of the method's declaring , plus the name of - /// the supplied . - ///

- ///

- /// Note that the declaring is that - /// that originally declared - /// the method, not necessarily the that is - /// currently exposing it. For example, - /// matches any subclass of 's - /// method. - ///

- ///
- /// The candidate method. - /// - /// The target (may be , - /// in which case the candidate must be taken - /// to be the 's declaring class). - /// - /// - /// if this this method matches statically. - /// - public override bool Matches(MethodInfo method, Type targetType) - { - string patt = method.DeclaringType.FullName + "." + method.Name; - for (int i = 0; i < this.Patterns.Length; ++i) - { - bool matched = Matches(patt, i); - if (matched) - { - return true; - } - } - return false; - } - - /// - /// Should the pointcut apply to the supplied - /// ? - /// - /// - ///

- /// In this instance, simply returns . - ///

- ///
- /// - /// The candidate . - /// - /// - /// if the advice should apply to the supplied - /// - /// - public bool Matches(Type type) - { - return true; - } - - #endregion - } + } + + #endregion + + #region Properties + + /// + /// The for this pointcut. + /// + /// + /// The current . + /// + public override ITypeFilter TypeFilter + { + get { return this; } + } + + /// + /// Convenience property for setting a single pattern. + /// + /// + /// Use this property or Patterns, not both. + /// + public virtual object Pattern + { + get { return (_patterns.Length > 0 ? _patterns[0] : null); } + set + { + AssertUtils.ArgumentNotNull(value, "Pattern"); + this.Patterns = new object[] { value }; + } + } + + /// + /// The regular expressions defining methods to match. + /// + /// + /// Matching will be the union of all these; if any match, + /// the pointcut matches. + /// + public virtual object[] Patterns + { + get { return _patterns; } + set + { + AssertUtils.ArgumentNotNull(value, "Patterns"); + this._patterns = value; + InitPatternRepresentation(this.Patterns); + } + } + + #endregion + + #region Methods + + /// + /// Populates a with + /// the data needed to serialize the target object. + /// + /// + /// The to populate + /// with data. + /// + /// + /// The destination (see ) + /// for this serialization. + /// + [SecurityPermission(SecurityAction.Demand, SerializationFormatter = true)] + public void GetObjectData(SerializationInfo info, StreamingContext context) + { + info.AddValue("Patterns", _patterns); + } + + /// + /// Subclasses must implement this to initialize regular expression pointcuts. + /// + /// + ///

+ /// Can be invoked multiple times. + ///

+ ///

+ /// This method will be invoked from the property, + /// and also on deserialization. + ///

+ ///
+ /// + /// The patterns to initialize. + /// + /// + /// In the case of an invalid pattern. + /// + protected abstract void InitPatternRepresentation(object[] patterns); + + /// + /// Does the pattern at the supplied + /// match this ? + /// + /// The pattern to match + /// The index of pattern. + /// + /// if there is a match. + /// + protected abstract bool Matches(string pattern, int patternIndex); + + /// + /// Does the supplied satisfy this matcher? + /// + /// + ///

+ /// Try to match the regular expression against the fully qualified name + /// of the method's declaring , plus the name of + /// the supplied . + ///

+ ///

+ /// Note that the declaring is that + /// that originally declared + /// the method, not necessarily the that is + /// currently exposing it. For example, + /// matches any subclass of 's + /// method. + ///

+ ///
+ /// The candidate method. + /// + /// The target (may be , + /// in which case the candidate must be taken + /// to be the 's declaring class). + /// + /// + /// if this this method matches statically. + /// + public override bool Matches(MethodInfo method, Type targetType) + { + string patt = string.Empty; + patt = String.Format("{0}.{1}", method.DeclaringType, method.Name); + +#if NET_2_0 + if (method.DeclaringType.IsGenericType) + { + patt = String.Format("{0}.{1}", ReflectionUtils.GetTypeFriendlyName(method.DeclaringType), method.Name); + } + else + { + patt = String.Format("{0}.{1}", method.DeclaringType, method.Name); + } +#endif + for (int i = 0; i < this.Patterns.Length; ++i) + { + bool matched = Matches(patt, i); + if (matched) + { + return true; + } + } + return false; + } + + /// + /// Should the pointcut apply to the supplied + /// ? + /// + /// + ///

+ /// In this instance, simply returns . + ///

+ ///
+ /// + /// The candidate . + /// + /// + /// if the advice should apply to the supplied + /// + /// + public bool Matches(Type type) + { + return true; + } + + #endregion + } } \ No newline at end of file diff --git a/src/Spring/Spring.Core/Util/ReflectionUtils.cs b/src/Spring/Spring.Core/Util/ReflectionUtils.cs index 4880ba55..c30b7d97 100644 --- a/src/Spring/Spring.Core/Util/ReflectionUtils.cs +++ b/src/Spring/Spring.Core/Util/ReflectionUtils.cs @@ -669,6 +669,33 @@ namespace Spring.Util return paramsType; } + + /// + /// Given the return its representation as + /// it would appear in the source code files. + /// + /// + /// Largely intended to handle generic types where .ToString() will typically return: + /// "System.Collections.Generic.List`1[System.Collections.Generic.Dictionary`2[System.String,System.Int32]]" + /// and this method will instead return: + /// "System.Collections.Generic.List<System.Collections.Generic.Dictionary<string,int>>" + /// + /// The type. + /// Friendly string representing the Type + public static string GetTypeFriendlyName(Type type) + { +#if MONO + //no csharp services in mono (verify this!) so have to fall back to returning the ToString() for now + //TODO: investigate whether there is another equivalent manner of providing this functionality under MONO + return type.ToString(); +#endif +#if NET_2_0 + return (new Microsoft.CSharp.CSharpCodeProvider()).GetTypeOutput(new System.CodeDom.CodeTypeReference(type)); +#endif + return type.ToString(); + } + + /// /// Does the given and/or it's superclasses /// have at least one or more methods with the given name (with any diff --git a/test/Spring/Spring.Aop.Tests/Aop/Support/AbstractRegularExpressionMethodPointcutTests.cs b/test/Spring/Spring.Aop.Tests/Aop/Support/AbstractRegularExpressionMethodPointcutTests.cs index cc1684f9..04dfaef0 100644 --- a/test/Spring/Spring.Aop.Tests/Aop/Support/AbstractRegularExpressionMethodPointcutTests.cs +++ b/test/Spring/Spring.Aop.Tests/Aop/Support/AbstractRegularExpressionMethodPointcutTests.cs @@ -74,12 +74,46 @@ namespace Spring.Aop.Support ExactMatchTests(pointcut); } - protected void ExactMatchTests(AbstractRegularExpressionMethodPointcut rpc) - { - // assumes rpc.setPattern("java.lang.Object.hashCode"); - Assert.IsTrue(rpc.Matches(typeof(object).GetMethod("GetHashCode"), typeof(int))); - Assert.IsFalse(rpc.Matches(typeof(object).GetMethod("GetType"), typeof(Type))); - } +#if NET_2_0 + protected void ExactMatchWithGenericTypeTests(AbstractRegularExpressionMethodPointcut rpc) + { + // assumes rpc.setPattern("System.Collections.Generic.List"); + Assert.IsTrue(rpc.Matches(typeof(System.Collections.Generic.List).GetMethod("Add"), typeof(int))); + Assert.IsFalse(rpc.Matches(typeof(System.Collections.Generic.List).GetMethod("GetType"), typeof(Type))); + } + + [Test] + public void ExactMatchWithGenericType() + { + pointcut.Pattern = "System.Collections.Generic.List.Add"; + ExactMatchWithGenericTypeTests(pointcut); + pointcut = (AbstractRegularExpressionMethodPointcut)SerializationTestUtils.SerializeAndDeserialize(pointcut); + ExactMatchWithGenericTypeTests(pointcut); + } + + [Test] + public void WildcardWithGenericType() + { + pointcut.Pattern = ".*List.Add"; + Assert.IsTrue(pointcut.Matches(typeof(System.Collections.Generic.List).GetMethod("Add"), typeof(int))); + Assert.IsFalse(pointcut.Matches(typeof(System.Collections.Generic.List).GetMethod("GetType"), typeof(Type))); + } + + [Test] + public void WildcardForOneClassWithGenericType() + { + pointcut.Pattern = "System.Collections.*"; + Assert.IsTrue(pointcut.Matches(typeof(System.Collections.Generic.List).GetMethod("Add"), typeof(int))); + Assert.IsFalse(pointcut.Matches(typeof(System.Collections.Generic.List).GetMethod("GetType"), typeof(Type))); + } +#endif + + protected void ExactMatchTests(AbstractRegularExpressionMethodPointcut rpc) + { + // assumes rpc.setPattern("java.lang.Object.hashCode"); + Assert.IsTrue(rpc.Matches(typeof(object).GetMethod("GetHashCode"), typeof(int))); + Assert.IsFalse(rpc.Matches(typeof(object).GetMethod("GetType"), typeof(Type))); + } [Test] public void Wildcard() @@ -88,7 +122,7 @@ namespace Spring.Aop.Support Assert.IsTrue(pointcut.Matches(typeof(object).GetMethod("GetHashCode"), typeof(int))); Assert.IsFalse(pointcut.Matches(typeof(object).GetMethod("GetType"), typeof(Type))); } - + [Test] public void WildcardForOneClass() { @@ -100,10 +134,11 @@ namespace Spring.Aop.Support [Test] public void MatchesObjectClass() { - pointcut.Pattern = "System.Object.*"; + pointcut.Pattern = "Object.*"; Assert.IsTrue(pointcut.Matches(typeof(Exception).GetMethod("GetHashCode"), typeof(TargetException))); // Doesn't match Assert.IsFalse(pointcut.Matches(typeof(Exception).GetMethod("ToString"), typeof(Exception))); } + } } diff --git a/test/Spring/Spring.Core.Tests/Util/ReflectionUtilsTests.cs b/test/Spring/Spring.Core.Tests/Util/ReflectionUtilsTests.cs index e9d4fae6..b2b6f23c 100644 --- a/test/Spring/Spring.Core.Tests/Util/ReflectionUtilsTests.cs +++ b/test/Spring/Spring.Core.Tests/Util/ReflectionUtilsTests.cs @@ -1222,6 +1222,15 @@ namespace Spring.Util Assert.AreEqual(ex, appEx); } +#if NET_2_0 + [Test] + public void CanGetFriendlyNamesForGenericTypes() + { + Type t = typeof(System.Collections.Generic.List>); + + Assert.AreEqual("System.Collections.Generic.List>", ReflectionUtils.GetTypeFriendlyName(t)); + } +#endif #endregion #region Helper Methods