using System;
using System.Xml;
using System.Threading;
using System.Diagnostics;
using System.Collections;
using System.ComponentModel;
using AnticipatingMinds.Genesis.Effectors;
namespace AnticipatingMinds.Genesis.KnowledgeManagement{
/// <summary>
/// Represnts a delegate to handle an event of rule properties being changed.
/// </summary>
public delegate void RulePropertiesChangedEventHandler(object sender, EventArgs e);
/// <summary>
/// Represents a knowledge base rule.
/// </summary>
/// <remarks>
/// Rule is the base class for all Knowledge Pack rules. Every rule that analyzes code dom must inherit from this class.
/// The base class provides couple of important fundumental services that are leveraged by
/// Knowledge Base. It also provides persistant state management & basic property window editing abilities.
/// </remarks>
public abstract class Rule
{
/// <summary>
/// Defines string keys for property names in IDictionary
/// </summary>
public class PropertyName
{
public const string Name = "Name";
public const string Description = "Description";
public const string HelpUrl = "HelpUrl";
public const string ApplicabilityScopes = "ApplicabilityScopes";
public const string IsDisabled = "IsRuleDisabled";
}
/// <summary>
/// Represents an event raised every time when one or more rule properties has been changed.
/// </summary>
/// <remarks>
/// Rule property can be changed by user by editing it in the properties grid or
/// by initiating rule properties dialog.
/// </remarks>
public event PropertyChangedEventHandler PropertyChanged;
/// <summary>
/// Initializes new Instance of <see cref="Rule"/> class.
/// </summary>
/// <param name="id">Unique identifier of the rule.</param>
/// <param name="templateId">an id of the template responsable for rule functionality.</param>
/// <param name="ruleProperties">A set of rule properties that has to be used for initializing new rule.</param>
protected Rule(string id, string templateId, IDictionary ruleProperties)
{
properties = new Hashtable(ruleProperties);
this.id = id;
this.templateId = templateId;
}
/// <summary>
/// Gets the rule template id.
/// </summary>
/// <value>
/// The value returned by this function identifies the template that implements rule functionality.
/// </value>
[BrowsableAttribute(false)]
public string TemplateId
{
get
{
return templateId;
}
}
/// <summary>
/// Gets rule unique identifier.
/// </summary>
/// <value>Rule id is unique rule identifier.</value>
[BrowsableAttribute(false)]
public string Id
{
get
{
return id;
}
}
/// <summary>
/// Gets or sets the name of the rule.
/// </summary>
/// <value>Name of the rule is human readable name, assigned by the user that describes what rule does.</value>
[CategoryAttribute("Appearance")]
public virtual string Name
{
get
{
return GetProperty(PropertyName.Name,String.Empty) as String;
}
set
{
SetProperty(PropertyName.Name,value == null ? String.Empty:value);
}
}
/// <summary>
/// Gets or sets rule description.
/// </summary>
/// <value>The value of this property specifies an extended description of the rule functionality.</value>
[CategoryAttribute("Misc")]
public virtual string Description
{
get
{
return GetProperty(PropertyName.Description,String.Empty) as String;
}
set
{
SetProperty(PropertyName.Description,value == null ? String.Empty:value);
}
}
/// <summary>
/// Gets or sets HelpUrl property.
/// </summary>
/// <value>Help url property contains a reference (HTTP or mshelp) to the material that provides
/// help for the rule functionality. When rule is just created this value is inherited from rule template but user can change it
/// to any value while editing rule properties.</value>
[CategoryAttribute("Misc")]
public virtual string HelpUrl
{
get
{
return GetProperty(PropertyName.HelpUrl,String.Empty) as String;
}
set
{
SetProperty(PropertyName.HelpUrl,value == null ? String.Empty:value);
}
}
[Browsable(false)]
public ApplicabilityScopeReadOnlyCollection ApplicabilityScopes
{
get
{
return GetProperty(PropertyName.ApplicabilityScopes) as ApplicabilityScopeReadOnlyCollection;
}
set
{
SetProperty(PropertyName.ApplicabilityScopes,value);
}
}
/// <summary>
/// Gets an array of types for applicable scopes.
/// </summary>
/// <remarks>
/// <para>
/// Applicability scope is a filter that can be applied to specify which elements must be analyzed and
/// which must be skiped.
/// </para>
/// <para>
/// To determine which scopes can be applied to a rule, rule must return an array of types of applicable
/// ApplicabilityScope types.
/// </para>
/// <para>
/// For example if rule analyzes if types are serializable - rule shall return the following
/// applicability scopes: Type,Class,Struct,Enum,File and Folder scopes.
/// </para>
/// <para>
/// If rule requires methods of a type to have a particular attribute rule must return the following scopes:
/// Method,File and Folder scopes.
/// </para>
/// </remarks>
[Browsable(false)]
public virtual Type[] ApplicableApplicabilityScopeTypes
{
get
{
return ruleApplicabilityScopes;
}
}
/// <summary>
/// Gets targeted code elements.
/// </summary>
/// <value>An array of different CodeDom types.</value>
/// <remarks>During analysis every code dom element is visited by analysis engine.
/// Very often rules are only intrested in inspecting a particular code element, class declaration, or field reference, to
/// improve performance rule can specify the types of code dom elements it wants to analyse and
/// than rules Analyze function will be only called for the specified elements.
/// This mechanism allows to improve overall analysis performance.
/// </remarks>
[BrowsableAttribute(false)]
public abstract Type[] TargetedCodeElements
{
get;
}
/// <summary>
/// Analyzes input element for rule compliance.
/// </summary>
/// <param name="targetedObject">Object that has to be inspected.</param>
/// <param name="cancelAnalysisEvent">An event that is set to the 'signaled' state when user cancel analysis.</param>
/// <returns>An array of all found violations.</returns>
/// <remarks>
/// This function is the heart of code analysis. targetObject is always of one of the types specified by <see cref="TargetedCodeElements"/> property.
/// If rules analysis is time consuming rule must check <paramref name="cancelAnalysisEvent"/> during anallysis process.
/// if <paramref name="cancelAnalysisEvent"/> becomes signaled, rule must abort analysis and return imediatly.
/// </remarks>
public abstract RuleViolation[] Analyze(object targetedObject,ManualResetEvent cancelAnalysisEvent);
/// <summary>
/// Corrects violations passed in <paramref name="ruleViolationCorrections"/> using appropriate VilolationCorrection option.
/// </summary>
/// <param name="ruleViolationCorrections">User selected violation corrections to be rendered.</param>
/// <param name="codeEffector">CodeEffector object that is used in making code changes.</param>
/// <param name="cancelCorrectionEvent">An event that becomes signaled if user cancels the correction.</param>
public abstract void Correct(RuleViolationCorrection[] ruleViolationCorrections,CodeEffector codeEffector, ManualResetEvent cancelCorrectionEvent);
public void Correct(RuleViolationCorrection ruleViolationCorrection,CodeEffector codeEffector, ManualResetEvent cancelCorrectionEvent)
{
Correct(new RuleViolationCorrection[] {ruleViolationCorrection},codeEffector, cancelCorrectionEvent);
}
/// <summary>
/// Gets a dictionary to store rule properties.
/// </summary>
/// <returns>A dictionary that stores rue properties.</returns>
/// <remarks>To set or to get properties always use <see cref="SetProperty"/> and <see cref="GetProperty"/> functions.
/// </remarks>
public virtual IDictionary GetProperties()
{
return properties as IDictionary;
}
/// <summary>
/// Initializes all Rule properties from a dictionary object.
/// </summary>
/// <param name="ruleProperties">Rule properties to initialize.</param>
public virtual void SetProperties(IDictionary ruleProperties)
{
properties.Clear();
foreach(object key in ruleProperties.Keys)
properties[key] = ruleProperties[key];
OnPropertyChanged(null);
}
[BrowsableAttribute(true)]
[CategoryAttribute("Behavior")]
public virtual bool IsDisabled
{
get
{
return (bool) GetProperty(Rule.PropertyName.IsDisabled,false);
}
set
{
SetProperty(Rule.PropertyName.IsDisabled,value);
}
}
/// <summary>
/// Raises an event when property is changed.
/// </summary>
/// <param name="propertyName">The name of the changed property.</param>
/// <remarks>
/// You should never call this function directly. Instead use <see cref="SetProperty"/> function to change property value.
/// </remarks>
protected virtual void OnPropertyChanged(string propertyName)
{
if(PropertyChanged != null)
PropertyChanged(this,new PropertyChangedEventArgs(propertyName));
}
/// <summary>
/// Sets rule property value.
/// </summary>
/// <param name="propertyName">Name of the property to set value.</param>
/// <param name="value">New value of the property.</param>
protected void SetProperty(string propertyName, object value)
{
if(properties.Contains(propertyName))
{
if(properties[propertyName] != null)
{
if(properties[propertyName].Equals(value))
return;
}
else
{
if(value == null)
return;
}
}
properties[propertyName] = value;
OnPropertyChanged(propertyName);
}
/// <summary>
/// Gets rule property value.
/// </summary>
/// <param name="propertyName">Property name.</param>
/// <returns>Property value if property found, null otherwise.</returns>
protected object GetProperty(string propertyName)
{
return GetProperty(propertyName,null);
}
/// <summary>
/// Gets rule property value.
/// </summary>
/// <param name="propertyName">Property name.</param>
/// <param name="defaultValue">Default value.</param>
/// <returns>
/// Returns property value if property is found and <paramref name="defaultValue"/> otherwise.
/// </returns>
protected object GetProperty(string propertyName,object defaultValue)
{
if(properties.Contains(propertyName))
return properties[propertyName];
else
return defaultValue;
}
/// <summary>
/// Checks if a particular element is in the rule applicability scope
/// </summary>
/// <param name="codeElement">Code element to verify.</param>
/// <returns>Returns true if codeElement is inside defined scope or no scope has been defined.</returns>
protected bool IsCodeElementInRuleApplicabilityScope(AnticipatingMinds.Genesis.CodeDOM.CodeElement codeElement)
{
//If no scopes defined - than by default return true - we are in.
if(ApplicabilityScopes == null)
return true;
if(ApplicabilityScopes.Count == 0)
return true;
///Here is the logic:
///If there is defined inclusion scope (one or more)
///The element must be defined in one of them
///The element must not be defined in any of the exclusion scopes if such defined
bool areInclusionScopesDefined = false;
bool isElementCoveredByInclusionScope = false;
foreach(ApplicabilityScope scope in ApplicabilityScopes)
{
if(!scope.IsActive)
continue;
if(scope.ScopeType == ApplicabilityScopeType.Inclusive)
{
areInclusionScopesDefined = true;
// do not check more than one inclusion scope if element is
//already a member of one of the inclusion scopes.
if(!isElementCoveredByInclusionScope && scope.IsElementInScope(codeElement))
isElementCoveredByInclusionScope = true;
}
else
{
//Exlusive scope.
if(!scope.IsElementInScope(codeElement))
return false;
}
}
if(areInclusionScopesDefined && !isElementCoveredByInclusionScope)
return false;
return true;
}
private Hashtable properties;
private string id;
private string templateId;
static Type[] ruleApplicabilityScopes = new Type[] {typeof(FileApplicabilityScope)};
}
}
|