#region License

/*
 * Copyright © 2002-2005 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

#region Imports

using System;
using System.Reflection;
using Spring.Core;

#endregion

namespace Spring.Aop.Support
{
	/// <summary>
	/// Pointcut and method matcher for use in simple <b>cflow</b>-style
	/// pointcuts.
	/// </summary>
	/// <remarks>
	/// <p>
	/// Evaluating such pointcuts is slower than evaluating normal pointcuts,
	/// but can nevertheless be useful in some cases. Of course, your mileage
	/// may vary as to what 'slower' actually means.
	/// </p>
	/// </remarks>
	/// <author>Rod Johnson</author>
	/// <author>Simon White (.NET)</author>
	[Serializable]
	public class ControlFlowPointcut : IPointcut, ITypeFilter, IMethodMatcher
	{
		#region Fields

		private Type _type;
		private string _methodName;
		private int _evaluationCount;

		#endregion

		#region Constructor (s) / Destructor

		/// <summary>
		/// Creates a new instance of the <see cref="ControlFlowPointcut"/>
		/// class.
		/// </summary>
		/// <param name="type">
		/// The class under which <b>all</b> control flows are to be matched.
		/// </param>
		public ControlFlowPointcut(Type type) : this(type, null)
		{
		}

		/// <summary>
		/// Construct a new pointcut that matches all calls below the
		/// given method in the given class.
		/// </summary>
		/// <remarks>
		/// <p>
		/// If the supplied <paramref name="methodName"/> is
		/// <see langword="null"/>, <b>all</b> control flows below the given
		/// class will be successfully matched.
		/// </p>
		/// </remarks>
		/// <param name="type">
		/// The class under which <b>all</b> control flows are to be matched.
		/// </param>
		/// <param name="methodName">
		/// The method name under which <b>all</b> control flows are to be matched.
		/// </param>
		public ControlFlowPointcut(Type type, string methodName)
		{
			_type = type;
			_methodName = methodName;
		}

		#endregion

		#region Properties

		/// <summary>
		/// The <see cref="Spring.Aop.ITypeFilter"/> for this pointcut.
		/// </summary>
		/// <value>
		/// The current <see cref="Spring.Aop.ITypeFilter"/>.
		/// </value>
		public ITypeFilter TypeFilter
		{
			get { return this; }
		}

		/// <summary>
		/// Gets the number of times this pointcut has been <i>evaluated</i>.
		/// </summary>
		/// <remarks>
		/// <p>
		/// Useful as a debugging aid.
		/// </p>
		/// <p>
		/// Note that this value is distinct from the number of times that this
		/// pointcut sucessfully matches a target method, in that a
		/// <see cref="ControlFlowPointcut"/> may be evaluated many times but
		/// never actually match even once.
		/// </p>
		/// </remarks>
		/// <value>
		/// The number of times this pointcut has been <i>evaluated</i>.
		/// </value>
		public int EvaluationCount
		{
			get { return _evaluationCount; }
		}

		/// <summary>
		/// The <see cref="Spring.Aop.IMethodMatcher"/> for this pointcut.
		/// </summary>
		/// <value>
		/// The current <see cref="Spring.Aop.IMethodMatcher"/>.
		/// </value>
		public IMethodMatcher MethodMatcher
		{
			get { return this; }
		}

		/// <summary>
		/// Is this a runtime pointcut?
		/// </summary>
		/// <remarks>
		/// <p>
		/// This implementation is a runtime pointcut, and so always returns
		/// <see langword="true"/>.
		/// </p>
		/// </remarks>
		/// <value>
		/// <see langword="true"/> if this is a runtime pointcut.
		/// </value>
		/// <seealso cref="Spring.Aop.IMethodMatcher.IsRuntime"/>
		public bool IsRuntime
		{
			get { return true; }
		}

		#endregion

		#region Methods

		/// <summary>
		/// Should the pointcut apply to the supplied <see cref="System.Type"/>?
		/// </summary>
		/// <remarks>
		/// <p>
		/// Subclasses are encouraged to override this method for greater
		/// filtering (and performance).
		/// </p>
		/// <p>
		/// This, the default, implementation always matches (returns
		/// <see langword="true"/>).
		/// </p>
		/// </remarks>
		/// <param name="type">The candidate target class.</param>
		/// <returns>
		/// <see langword="true"/> if the advice should apply to the supplied
		/// <paramref name="type"/>
		/// </returns>
		public virtual bool Matches(Type type)
		{
			return true;
		}

		/// <summary>
		/// Does the supplied <paramref name="method"/> satisfy this matcher?
		/// Perform static checking. If this returns false, or if the isRuntime() method
		/// returns false, no runtime check will be made.
		/// </summary>
		/// <remarks>
		/// <p>
		/// Subclasses are encouraged to override this method if it is possible
		/// to filter out some candidate classes.
		/// </p>
		/// <p>
		/// This, the default, implementation always matches (returns
		/// <see langword="true"/>). This means that the three argument
		/// <see cref="Spring.Aop.IMethodMatcher.Matches(MethodInfo, Type, object[])"/>
		/// method will always be invoked.
		/// </p>
		/// </remarks>
		/// <param name="method">The candidate method.</param>
		/// <param name="targetType">
		/// The target class (may be <see langword="null"/>, in which case the
		/// candidate class must be taken to be the <paramref name="method"/>'s
		/// declaring class).
		/// </param>
		/// <returns>
		/// <see langword="true"/> if this this method matches statically.
		/// </returns>
		/// <seealso cref="Spring.Aop.IMethodMatcher.Matches(MethodInfo, Type)"/>
		public virtual bool Matches(MethodInfo method, Type targetType)
		{
			return true;
		}

		/// <summary>
		/// Is there a runtime (dynamic) match for the supplied
		/// <paramref name="method"/>?
		/// </summary>
		/// <remarks>
		/// <p>
		/// Subclasses are encouraged to override this method if it is possible
		/// to filter out some candidate classes.
		/// </p>
		/// </remarks>
		/// <param name="method">The candidate method.</param>
		/// <param name="targetType">The target class.</param>
		/// <param name="args">The arguments to the method</param>
		/// <returns>
		/// <see langword="true"/> if there is a runtime match.</returns>
		/// <seealso cref="Spring.Aop.IMethodMatcher.Matches(MethodInfo, Type, object[])"/>
		public virtual bool Matches(MethodInfo method, Type targetType, object[] args)
		{
			++_evaluationCount;
			IControlFlow cflow = ControlFlowFactory.CreateControlFlow();
			return (_methodName != null)
				? cflow.Under(_type, _methodName)
				: cflow.Under(_type);
		}

		#endregion
	}
}