Code:
/ 4.0 / 4.0 / untmp / DEVDIV_TFS / Dev10 / Releases / RTMRel / ndp / cdf / src / WF / Activities / Rules / RuleAttributes.cs / 1305376 / RuleAttributes.cs
// ----------------------------------------------------------------------------
// Copyright (C) 2005 Microsoft Corporation All Rights Reserved
// ---------------------------------------------------------------------------
using System;
using System.CodeDom;
using System.Collections.Generic;
using System.Globalization;
using System.Workflow.ComponentModel;
using System.Workflow.ComponentModel.Compiler;
using System.Reflection;
using System.Workflow.Activities.Common;
namespace System.Workflow.Activities.Rules
{
public enum RuleAttributeTarget
{
Parameter,
This
}
public abstract class RuleAttribute : Attribute
{
internal abstract bool Validate(RuleValidation validation, MemberInfo member, Type contextType, ParameterInfo[] parameters);
internal abstract void Analyze(RuleAnalysis analysis, MemberInfo member, CodeExpression targetExpression, RulePathQualifier targetQualifier, CodeExpressionCollection argumentExpressions, ParameterInfo[] parameters, List attributedExpressions);
}
public abstract class RuleReadWriteAttribute : RuleAttribute
{
private RuleAttributeTarget attributeTarget;
private string attributePath;
protected RuleReadWriteAttribute(string path, RuleAttributeTarget target)
{
this.attributeTarget = target;
this.attributePath = path;
}
public string Path
{
get { return attributePath; }
}
public RuleAttributeTarget Target
{
get { return attributeTarget; }
}
internal override bool Validate(RuleValidation validation, MemberInfo member, Type contextType, ParameterInfo[] parameters)
{
ValidationError error = null;
string message = null;
if (string.IsNullOrEmpty(attributePath))
{
// It is allowed to pass null or the empty string to [RuleRead] or [RuleWrite]. This
// is how you indicate that a method or property has no dependencies or side effects.
return true;
}
bool valid = true;
string[] parts = attributePath.Split('/');
// Check the first part.
string firstPart = parts[0];
int startOfRelativePortion = 0;
if (attributeTarget == RuleAttributeTarget.This)
{
// When target is "This", the path is allowed to start with the token "this". It is
// then skipped for the rest of the validation, and the contextType remains what it
// was when passed in.
if (firstPart == "this")
++startOfRelativePortion;
}
else
{
// When target is "Parameter", the path must start with the name of a parameter.
bool found = false;
for (int p = 0; p < parameters.Length; ++p)
{
ParameterInfo param = parameters[p];
if (param.Name == firstPart)
{
found = true;
// The context type is the parameter type.
contextType = param.ParameterType;
break;
}
}
if (!found)
{
message = string.Format(CultureInfo.CurrentCulture, Messages.InvalidRuleAttributeParameter, firstPart, member.Name);
error = new ValidationError(message, ErrorNumbers.Error_InvalidRuleAttributeParameter);
error.UserData[RuleUserDataKeys.ErrorObject] = this;
validation.AddError(error);
return false;
}
++startOfRelativePortion;
}
int numParts = parts.Length;
// Check the last part. The last part is allowed to be empty, or "*".
string lastPart = parts[numParts - 1];
if (string.IsNullOrEmpty(lastPart) || lastPart == "*")
numParts -= 1;
// Check the rest of the parts.
Type currentType = contextType;
for (int i = startOfRelativePortion; i < numParts; ++i)
{
// Can't have embedded "*" wildcards.
if (parts[i] == "*")
{
// The "*" occurred in the middle of the path, which is a no-no.
error = new ValidationError(Messages.InvalidWildCardInPathQualifier, ErrorNumbers.Error_InvalidWildCardInPathQualifier);
error.UserData[RuleUserDataKeys.ErrorObject] = this;
validation.AddError(error);
valid = false;
break;
}
// Skip array types.
while (currentType.IsArray)
currentType = currentType.GetElementType();
// Make sure the member exists in the current type.
BindingFlags bindingFlags = BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static | BindingFlags.FlattenHierarchy;
if (validation.AllowInternalMembers(currentType))
bindingFlags |= BindingFlags.NonPublic;
FieldInfo field = currentType.GetField(parts[i], bindingFlags);
if (field != null)
{
currentType = field.FieldType;
}
else
{
PropertyInfo property = currentType.GetProperty(parts[i], bindingFlags);
if (property != null)
{
currentType = property.PropertyType;
}
else
{
message = string.Format(CultureInfo.CurrentCulture, Messages.UpdateUnknownFieldOrProperty, parts[i]);
error = new ValidationError(message, ErrorNumbers.Error_UnknownFieldOrProperty);
error.UserData[RuleUserDataKeys.ErrorObject] = this;
validation.AddError(error);
valid = false;
break;
}
}
}
return valid;
}
internal void AnalyzeReadWrite(RuleAnalysis analysis, CodeExpression targetExpression, RulePathQualifier targetQualifier, CodeExpressionCollection argumentExpressions, ParameterInfo[] parameters, List attributedExpressions)
{
if (string.IsNullOrEmpty(attributePath))
{
// If the suffix is null or empty, this means the RuleAttributeTarget has no dependencies.
if (attributeTarget == RuleAttributeTarget.This)
{
// The target object has no dependencies.
attributedExpressions.Add(targetExpression);
}
else if (attributeTarget == RuleAttributeTarget.Parameter)
{
// ALL arguments have no dependencies.
for (int i = 0; i < argumentExpressions.Count; ++i)
attributedExpressions.Add(argumentExpressions[i]);
}
}
else
{
string suffix = attributePath;
bool isRead = !analysis.ForWrites;
bool isWrite = analysis.ForWrites;
if (attributeTarget == RuleAttributeTarget.This)
{
// Target is "This", so perform the analysis on the target expression.
// Remove the optional "this/" token if present.
string optionalPrefix = "this/";
if (suffix.StartsWith(optionalPrefix, StringComparison.Ordinal))
suffix = suffix.Substring(optionalPrefix.Length);
RuleExpressionWalker.AnalyzeUsage(analysis, targetExpression, isRead, isWrite, new RulePathQualifier(suffix, targetQualifier));
attributedExpressions.Add(targetExpression);
}
else if (attributeTarget == RuleAttributeTarget.Parameter)
{
string paramName = null;
int firstSlash = suffix.IndexOf('/');
if (firstSlash >= 0)
{
paramName = suffix.Substring(0, firstSlash);
suffix = suffix.Substring(firstSlash + 1);
}
else
{
paramName = suffix;
suffix = null;
}
// Find the ParameterInfo that corresponds to this attribute path.
ParameterInfo param = Array.Find(parameters,
delegate(ParameterInfo p) { return p.Name == paramName; });
if (param != null)
{
RulePathQualifier qualifier = string.IsNullOrEmpty(suffix) ? null : new RulePathQualifier(suffix, null);
// 99.9% of the time, the parameter usage attribute only applies to one argument. However,
// if this attribute corresponds to the last parameter, then just assume that all the trailing
// arguments correspond. (In other words, if the caller passed more arguments then there
// are parameters, we assume it was a params array.)
//
// Usually this loop will only execute once.
int end = param.Position + 1;
if (param.Position == parameters.Length - 1)
end = argumentExpressions.Count;
for (int i = param.Position; i < end; ++i)
{
CodeExpression argExpr = argumentExpressions[i];
RuleExpressionWalker.AnalyzeUsage(analysis, argExpr, isRead, isWrite, qualifier);
attributedExpressions.Add(argExpr);
}
}
}
}
}
}
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Method, AllowMultiple = true)]
public sealed class RuleReadAttribute : RuleReadWriteAttribute
{
public RuleReadAttribute(string path, RuleAttributeTarget target)
: base(path, target)
{
}
public RuleReadAttribute(string path)
: base(path, RuleAttributeTarget.This)
{
}
internal override void Analyze(RuleAnalysis analysis, MemberInfo member, CodeExpression targetExpression, RulePathQualifier targetQualifier, CodeExpressionCollection argumentExpressions, ParameterInfo[] parameters, List attributedExpressions)
{
// A RuleRead attribute is only applicable if we're analyzing for reads.
if (analysis.ForWrites)
return;
base.AnalyzeReadWrite(analysis, targetExpression, targetQualifier, argumentExpressions, parameters, attributedExpressions);
}
}
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Method, AllowMultiple = true)]
public sealed class RuleWriteAttribute : RuleReadWriteAttribute
{
public RuleWriteAttribute(string path, RuleAttributeTarget target)
: base(path, target)
{
}
public RuleWriteAttribute(string path)
: base(path, RuleAttributeTarget.This)
{
}
internal override void Analyze(RuleAnalysis analysis, MemberInfo member, CodeExpression targetExpression, RulePathQualifier targetQualifier, CodeExpressionCollection argumentExpressions, ParameterInfo[] parameters, List attributedExpressions)
{
// A RuleWrite attribute is only applicable if we're analyzing for writes.
if (!analysis.ForWrites)
return;
base.AnalyzeReadWrite(analysis, targetExpression, targetQualifier, argumentExpressions, parameters, attributedExpressions);
}
}
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Method, AllowMultiple = true)]
public sealed class RuleInvokeAttribute : RuleAttribute
{
private string methodInvoked;
public RuleInvokeAttribute(string methodInvoked)
{
this.methodInvoked = methodInvoked;
}
public string MethodInvoked
{
get { return methodInvoked; }
}
internal override bool Validate(RuleValidation validation, MemberInfo member, Type contextType, ParameterInfo[] parameters)
{
Stack methodStack = new Stack();
methodStack.Push(member);
bool result = ValidateInvokeAttribute(validation, member, contextType, methodStack);
methodStack.Pop();
return result;
}
private bool ValidateInvokeAttribute(RuleValidation validation, MemberInfo member, Type contextType, Stack methodStack)
{
string message;
ValidationError error;
if (string.IsNullOrEmpty(methodInvoked))
{
// Invoked method or property name was null or empty.
message = string.Format(CultureInfo.CurrentCulture, Messages.AttributeMethodNotFound, member.Name, this.GetType().Name, Messages.NullValue);
error = new ValidationError(message, ErrorNumbers.Warning_RuleAttributeNoMatch, true);
error.UserData[RuleUserDataKeys.ErrorObject] = this;
validation.AddError(error);
return false;
}
bool valid = true;
// Go through all the methods and properties on the target context,
// looking for all the ones that match the name on the attribute.
MemberInfo[] members = contextType.GetMember(methodInvoked, MemberTypes.Method | MemberTypes.Property, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy);
if (members == null || members.Length == 0)
{
// Invoked method or property didn't exist.
message = string.Format(CultureInfo.CurrentCulture, Messages.AttributeMethodNotFound, member.Name, this.GetType().Name, methodInvoked);
error = new ValidationError(message, ErrorNumbers.Warning_RuleAttributeNoMatch, true);
error.UserData[RuleUserDataKeys.ErrorObject] = this;
validation.AddError(error);
valid = false;
}
else
{
for (int i = 0; i < members.Length; ++i)
{
MemberInfo mi = members[i];
if (!methodStack.Contains(mi)) // Prevent recursion
{
methodStack.Push(mi);
object[] attrs = mi.GetCustomAttributes(typeof(RuleAttribute), true);
if (attrs != null && attrs.Length != 0)
{
foreach (RuleAttribute invokedRuleAttr in attrs)
{
RuleReadWriteAttribute readWriteAttr = invokedRuleAttr as RuleReadWriteAttribute;
if (readWriteAttr != null)
{
// This read/write attribute may not specify a target of "Parameter", since
// we can't map from the invoker's parameters to the invokee's parameters.
if (readWriteAttr.Target == RuleAttributeTarget.Parameter)
{
message = string.Format(CultureInfo.CurrentCulture, Messages.InvokeAttrRefersToParameterAttribute, mi.Name);
error = new ValidationError(message, ErrorNumbers.Error_InvokeAttrRefersToParameterAttribute, true);
error.UserData[RuleUserDataKeys.ErrorObject] = this;
validation.AddError(error);
valid = false;
}
else
{
// Validate the read/write attribute normally.
readWriteAttr.Validate(validation, mi, contextType, null);
}
}
else
{
RuleInvokeAttribute invokeAttr = (RuleInvokeAttribute)invokedRuleAttr;
invokeAttr.ValidateInvokeAttribute(validation, mi, contextType, methodStack);
}
}
}
methodStack.Pop();
}
}
}
return valid;
}
internal override void Analyze(RuleAnalysis analysis, MemberInfo member, CodeExpression targetExpression, RulePathQualifier targetQualifier, CodeExpressionCollection argumentExpressions, ParameterInfo[] parameters, List attributedExpressions)
{
Stack methodStack = new Stack();
methodStack.Push(member);
AnalyzeInvokeAttribute(analysis, member.DeclaringType, methodStack, targetExpression, targetQualifier, argumentExpressions, parameters, attributedExpressions);
methodStack.Pop();
}
private void AnalyzeInvokeAttribute(RuleAnalysis analysis, Type contextType, Stack methodStack, CodeExpression targetExpression, RulePathQualifier targetQualifier, CodeExpressionCollection argumentExpressions, ParameterInfo[] parameters, List attributedExpressions)
{
// Go through all the methods and properties on the target context,
// looking for all the ones that match the name on the attribute.
MemberInfo[] members = contextType.GetMember(methodInvoked, MemberTypes.Method | MemberTypes.Property, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy);
for (int m = 0; m < members.Length; ++m)
{
MemberInfo mi = members[m];
if (!methodStack.Contains(mi)) // Prevent recursion
{
methodStack.Push(mi);
object[] attrs = mi.GetCustomAttributes(typeof(RuleAttribute), true);
if (attrs != null && attrs.Length != 0)
{
RuleAttribute[] ruleAttrs = (RuleAttribute[])attrs;
for (int i = 0; i < ruleAttrs.Length; ++i)
{
RuleAttribute ruleAttr = ruleAttrs[i];
RuleReadWriteAttribute readWriteAttr = ruleAttr as RuleReadWriteAttribute;
if (readWriteAttr != null)
{
// Just analyze the read/write attribute normally.
readWriteAttr.Analyze(analysis, mi, targetExpression, targetQualifier, argumentExpressions, parameters, attributedExpressions);
}
else
{
RuleInvokeAttribute invokeAttr = (RuleInvokeAttribute)ruleAttr;
invokeAttr.AnalyzeInvokeAttribute(analysis, contextType, methodStack, targetExpression, targetQualifier, argumentExpressions, parameters, attributedExpressions);
}
}
}
methodStack.Pop();
}
}
}
}
}
// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
// Copyright (c) Microsoft Corporation. All rights reserved.
Link Menu

This book is available now!
Buy at Amazon US or
Buy at Amazon UK
- PeerDefaultCustomResolverClient.cs
- CallContext.cs
- ISAPIApplicationHost.cs
- ScriptControlDescriptor.cs
- DataServiceCollectionOfT.cs
- Vector3DKeyFrameCollection.cs
- dbenumerator.cs
- ComEventsInfo.cs
- CategoryValueConverter.cs
- XmlSerializerAssemblyAttribute.cs
- Freezable.cs
- HttpServerUtilityWrapper.cs
- TreeNodeCollection.cs
- FileLoadException.cs
- HitTestDrawingContextWalker.cs
- StateRuntime.cs
- PointF.cs
- ViewCellRelation.cs
- CultureInfoConverter.cs
- CompoundFileStorageReference.cs
- TemplateApplicationHelper.cs
- DataGridViewBindingCompleteEventArgs.cs
- XslNumber.cs
- GenericPrincipal.cs
- TemplateControlBuildProvider.cs
- TableLayoutColumnStyleCollection.cs
- XmlWriterDelegator.cs
- PopupControlService.cs
- FormViewDeletedEventArgs.cs
- TextEditorCopyPaste.cs
- ProviderIncompatibleException.cs
- OleDbConnectionInternal.cs
- SafeNativeMethods.cs
- ToolBarButtonClickEvent.cs
- PointAnimation.cs
- AvTrace.cs
- TextDocumentView.cs
- DataKey.cs
- WebPartConnectionsConnectVerb.cs
- TrackingServices.cs
- SoapCommonClasses.cs
- Substitution.cs
- BasicHttpMessageCredentialType.cs
- PointLightBase.cs
- ErrorHandler.cs
- DesignerDataParameter.cs
- SimpleType.cs
- DiagnosticTraceSource.cs
- RegexGroupCollection.cs
- PropertyEmitterBase.cs
- DataGridRowHeader.cs
- Avt.cs
- ProfileBuildProvider.cs
- XmlValueConverter.cs
- XXXOnTypeBuilderInstantiation.cs
- AnimationStorage.cs
- RecordManager.cs
- TextEditorMouse.cs
- DbSource.cs
- ListBindingHelper.cs
- ButtonBase.cs
- propertyentry.cs
- UniformGrid.cs
- UnsafeNativeMethods.cs
- UserNameSecurityToken.cs
- RedirectionProxy.cs
- DnsPermission.cs
- ServiceModelActivity.cs
- ExtendedProperty.cs
- PanelStyle.cs
- AssemblyCache.cs
- RelatedPropertyManager.cs
- DeadCharTextComposition.cs
- FixedFindEngine.cs
- IndentTextWriter.cs
- InfoCardHelper.cs
- SmtpNegotiateAuthenticationModule.cs
- ItemCheckedEvent.cs
- SafeReversePInvokeHandle.cs
- SqlConnectionFactory.cs
- FileSystemInfo.cs
- LiteralControl.cs
- ThreadPool.cs
- WsdlBuildProvider.cs
- Transform.cs
- DefaultTextStoreTextComposition.cs
- UInt32Converter.cs
- MachineSettingsSection.cs
- AssertValidation.cs
- CommandTreeTypeHelper.cs
- XmlEncoding.cs
- HtmlAnchor.cs
- RowSpanVector.cs
- GeometryCombineModeValidation.cs
- GeneralTransform2DTo3D.cs
- CultureSpecificCharacterBufferRange.cs
- AttachmentService.cs
- SqlInternalConnectionTds.cs
- StaticDataManager.cs
- SystemEvents.cs