using System;
using System.Xml;
using System.Collections;
using System.Collections.Specialized;
using AnticipatingMinds.CommonUIControls;
using AnticipatingMinds.Genesis.CodeDOM;
using AnticipatingMinds.Genesis.KnowledgeManagement;
using AnticipatingMinds.Genesis.CodeDOM.Utilities;
namespace AnticipatingMinds.KnowledgePack.Threading{
/// <summary>
/// Summary description for StaticDoNotChangeClassState.
/// </summary>
public class StaticDoNotChangeClassStateRuleTemplate : RuleTemplate
{
public StaticDoNotChangeClassStateRuleTemplate(string templateId, IDictionary templateProperties):base(templateId,templateProperties)
{
KnowledgePackLicense.Validate();
}
public override string Name
{
get
{
return ResourceManager.GetLocalizedString("StaticDoNotChangeClassStateRuleTemplate|Name");
}
}
public override string Description
{
get
{
return ResourceManager.GetLocalizedString("StaticDoNotChangeClassStateRuleTemplate|Description");
}
}
public override Rule CreateRule(string ruleId, IDictionary ruleProperties)
{
// Todo: Add multiple rule types here from XML properties
// probably a case statement
return new StaticDoNotChangeClassStateRule(ruleId,Id,ruleProperties);
}
}
/// <summary>
/// Avoid providing static methods that alter static state.
/// </summary>
/// <remarks>
/// Avoid providing static methods that alter static state.
/// In common server scenarios, static state is shared across requests,
/// which means multiple threads can execute that code at the same time.
/// This opens up the possibility for threading bugs. Consider using a
/// design pattern that encapsulates data into instances that are not shared
/// across requests.
/// </remarks>
/// <url>http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpgenref/html/cpconthreadingdesignguidelines.asp</url>
public class StaticDoNotChangeClassStateRule : Rule
{
internal StaticDoNotChangeClassStateRule(string id, string templateId, IDictionary ruleProperties) : base(id,templateId,ruleProperties)
{
}
private struct RuleData
{
public CodeTypeDeclaration typeDeclaration;
public IDictionary staticMethods;
public RuleViolationCollection violations;
}
/// <summary>
/// Builds a collection of static type members. When done associates a collection with type
/// declaration code element in 'TypeStaticMembersByName' dictionary entry.
/// If collection already associated with type declaration - return
/// existant colletion.
/// </summary>
/// <param name="typeDeclaration"></param>
/// <returns></returns>
private IDictionary GetStaticTypeMembers(CodeTypeDeclaration typeDeclaration)
{
//Build a collection of static type members. When done associate such a collection with type code element to
//avoid uneccessary rebuild when examining different members of the same type.
if(typeDeclaration.ApplicationData.Contains("TypeStaticMembersByName"))
return typeDeclaration.ApplicationData["TypeStaticMembersByName"] as IDictionary;
IDictionary staticMembersByName = (new Hashtable()) as IDictionary;
typeDeclaration.ApplicationData["TypeStaticMembersByName"] = staticMembersByName;
CodeTypeMemberDeclarationCollection typeMembers = null;
if(typeDeclaration is CodeClassDeclaration)
typeMembers = (typeDeclaration as CodeClassDeclaration).Members;
if(typeDeclaration is CodeInterfaceDeclaration)
typeMembers = (typeDeclaration as CodeInterfaceDeclaration).Members;
if(typeDeclaration is CodeStructDeclaration)
typeMembers = (typeDeclaration as CodeStructDeclaration).Members;
if(typeMembers != null)
{
foreach(CodeTypeMemberDeclaration typeMember in typeMembers)
{
StringCollection declaredNames = new StringCollection();
if((typeMember.Modifiers & CodeTypeMemberDeclaration.MemberDeclarationModifiers.Static) != 0)
{
if(typeMember is CodeTypeEventDeclaration)
{
declaredNames.Add((typeMember as CodeTypeEventDeclaration).Name);
}
if(typeMember is CodeTypePropertyDeclaration)
{
declaredNames.Add((typeMember as CodeTypePropertyDeclaration).Name);
}
if(typeMember is CodeTypeMethodDeclaration)
{
declaredNames.Add((typeMember as CodeTypeMethodDeclaration).Name);
}
if(typeMember is CodeTypeEventDeclaration)
{
declaredNames.Add((typeMember as CodeTypeEventDeclaration).Name);
}
if(typeMember is CodeTypeFieldDeclaration)
{
foreach(CodeVariableDeclarationMember declarationMember in (typeMember as CodeTypeFieldDeclaration).DeclaredFields)
declaredNames.Add(declarationMember.Name);
}
if(typeMember is CodeTypeEventListDeclaration)
{
foreach(CodeVariableDeclarationMember declarationMember in (typeMember as CodeTypeEventListDeclaration).DeclaredEvents)
declaredNames.Add(declarationMember.Name);
}
foreach(string name in declaredNames)
if(!staticMembersByName.Contains(name))
staticMembersByName.Add(name,typeMember);
}
}
}
return staticMembersByName;
}
/// <summary>
///
/// </summary>
/// <param name="targetedObject"></param>
/// <param name="cancelAnalysisEvent"></param>
/// <returns></returns>
public override RuleViolation[] Analyze(object targetedObject, System.Threading.ManualResetEvent cancelAnalysisEvent)
{
CodeTypeMemberDeclaration typeMemberDeclaration = targetedObject as CodeTypeMemberDeclaration;
if(!IsCodeElementInRuleApplicabilityScope(typeMemberDeclaration))
return emptyViolationsList;
//Only intrested in static methods
if((typeMemberDeclaration.Modifiers & CodeTypeMemberDeclaration.MemberDeclarationModifiers.Static) == 0)
return emptyViolationsList;
if(!(typeMemberDeclaration is CodeTypeMethodDeclaration ||
typeMemberDeclaration is CodeTypeOperatorDeclaration ||
typeMemberDeclaration is CodeTypePropertyDeclaration))
return emptyViolationsList;
//If type is missing - something wron=g with code dom. But do not fail -
//just stop the analysis
if(typeMemberDeclaration.DeclaringType == null)
return emptyViolationsList;
//Only intrested in public & internal methods. It is ok if private method changes the state
//of the class. it is not ok if it is allowed to do from outside.
if((typeMemberDeclaration.Modifiers & CodeTypeMemberDeclaration.MemberDeclarationModifiers.Private) != 0)
return emptyViolationsList;
RuleData ruleData = new RuleData();
ruleData.staticMethods = GetStaticTypeMembers(typeMemberDeclaration.DeclaringType);
ruleData.violations = new RuleViolationCollection();
ruleData.typeDeclaration = typeMemberDeclaration.DeclaringType;
CodeDomWalkerContext walkerContex = new CodeDomWalkerContext();
walkerContex.ExcludeCodeElement(typeMemberDeclaration.DeclaringType);
CodeDomWalker.WalkCodeElement( typeMemberDeclaration , new CodeDomWalker.WalkerCallback(this.MyWalkerCallback), walkerContex,ruleData);
if(ruleData.violations.Count == 0)
return emptyViolationsList;
else
return ruleData.violations.ToArray();
}
private CodeDomWalker.WalkerCallbackReturn MyWalkerCallback(CodeElement codeElement, CodeDomWalker.CallBackNotificationType notificationType, CodeDomWalkerContext walkerContext,object applicationData)
{
if(notificationType == CodeDomWalker.CallBackNotificationType.OnElement)
{
if (codeElement is CodeTypeConstructorDeclaration)
if(((codeElement as CodeTypeConstructorDeclaration).Modifiers & CodeTypeConstructorDeclaration.MemberDeclarationModifiers.Static) != 0)
return CodeDomWalker.WalkerCallbackReturn.NextSibling;
if(codeElement is CodeBinaryExpression)
{
CodeBinaryExpression binaryExpression = codeElement as CodeBinaryExpression;
//If it is not an asignment - we are not intrested.
if(binaryExpression.Operator != CodeBinaryOperatorType.Assign)
return CodeDomWalker.WalkerCallbackReturn.Next;
//CodeNamedReferenceExpression is base for property or filed or event
//or generally assignable expression. If it is not CodeNamedReferenceExpression
//chances are it is not assignable.
if(!(binaryExpression.LeftOperand is CodeNamedReferenceExpression))
return CodeDomWalker.WalkerCallbackReturn.Next;
CodeNamedReferenceExpression namedReference = binaryExpression.LeftOperand as CodeNamedReferenceExpression;
//Static access can be specified in two forms
// 1) myStaticTypemember = <expression>
// 2) <TypeName>.myStaticTypemember = <expression>.
// 3) <FullTypeName>.myStaticTypemember = <expression>.
string targetReferenceName = string.Empty;
//If there i no reference or reference is another NamedExpression
//get the string representation of chained named expressions that
//constitute target.
//After initial parsing parser may not recognize type full name and represent
//it as chaine of UnresolvedReferenceExpressions.
if(namedReference.TargetObject == null || namedReference.TargetObject is CodeNamedReferenceExpression)
targetReferenceName = CodeNamedReferenceExpressionUtils.GetTargetReferencesAsString(namedReference.TargetObject as CodeNamedReferenceExpression);
//Or parser may recognize reference to a type and identify it as such.
if(namedReference.TargetObject != null && namedReference.TargetObject is CodeTypeReferenceExpression)
targetReferenceName = (namedReference.TargetObject as CodeTypeReferenceExpression).ReferencedType.TypeName;
RuleData ruleData = (RuleData)applicationData;
//If there is no target ot target is our type
//look if accessed member is static - (it must be but let's make sure!)
if(targetReferenceName.Length == 0 ||
targetReferenceName == ruleData.typeDeclaration.Name ||
targetReferenceName == ruleData.typeDeclaration.FullName)
{
if(ruleData.staticMethods.Contains(namedReference.Name))
{
RuleViolation violation = new RuleViolation(this,
string.Format(ResourceManager.GetLocalizedString( "StaticDoNotChangeClassStateViolation|Description"),namedReference.Name),
codeElement);
violation.Severity = RuleViolation.ViolationSeverity.Suggestion;
ruleData.violations.Add(violation);
}
}
}
}
return CodeDomWalker.WalkerCallbackReturn.Next;
}
public override void Correct(RuleViolationCorrection[] requestedCorrections, AnticipatingMinds.Genesis.Effectors.CodeEffector codeEffector, System.Threading.ManualResetEvent cancelCorrectionEvent)
{
//No correction.
}
public override Type[] TargetedCodeElements
{
get
{
return targtedCodeElements;
}
}
public override Type[] ApplicableApplicabilityScopeTypes
{
get
{
return applicableApplicabilityScopeTypes;
}
}
private static Type[] applicableApplicabilityScopeTypes = {typeof(MethodApplicabilityScope),typeof(PropertyApplicabilityScope),typeof(EventApplicabilityScope),typeof(FileApplicabilityScope)};
private Type[] targtedCodeElements = new Type[1] {typeof(CodeTypeMemberDeclaration)};
private static RuleViolation[] emptyViolationsList = new RuleViolation[0];
}
}
|