6f80c6056367643e30650c77f1e3f0c0ad3a3181
[mono.git] / mcs / class / referencesource / System.Workflow.ComponentModel / Shared / ValidationHelpers.cs
1 // Copyright (c) Microsoft Corporation. All rights reserved. 
2 //  
3 // THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, 
4 // WHETHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED 
5 // WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE. 
6 // THE ENTIRE RISK OF USE OR RESULTS IN CONNECTION WITH THE USE OF THIS CODE 
7 // AND INFORMATION REMAINS WITH THE USER. 
8
9
10 /*********************************************************************
11  * NOTE: A copy of this file exists at: WF\Activities\Common
12  * The two files must be kept in sync.  Any change made here must also
13  * be made to WF\Activities\Common\ValidationHelpers.cs
14 *********************************************************************/
15 namespace System.Workflow.ComponentModel.Compiler
16 {
17     #region Imports
18
19     using System;
20     using System.Diagnostics;
21     using System.Collections;
22     using System.Collections.Generic;
23     using System.Reflection;
24     using System.CodeDom.Compiler;
25     using System.Workflow.ComponentModel.Design;
26     using System.Workflow.ComponentModel.Compiler;
27     using System.Collections.Specialized;
28     using System.ComponentModel.Design.Serialization;
29     #endregion
30
31     internal static class ValidationHelpers
32     {
33         #region Validation & helpers for ID and Type
34
35         internal static void ValidateIdentifier(IServiceProvider serviceProvider, string identifier)
36         {
37             if (serviceProvider == null)
38                 throw new ArgumentNullException("serviceProvider");
39
40             SupportedLanguages language = CompilerHelpers.GetSupportedLanguage(serviceProvider);
41             CodeDomProvider provider = CompilerHelpers.GetCodeDomProvider(language);
42             if (language == SupportedLanguages.CSharp && identifier.StartsWith("@", StringComparison.Ordinal) ||
43                 language == SupportedLanguages.VB && identifier.StartsWith("[", StringComparison.Ordinal) && identifier.EndsWith("]", StringComparison.Ordinal) ||
44                 !provider.IsValidIdentifier(identifier))
45             {
46                 throw new Exception(SR.GetString(SR.Error_InvalidLanguageIdentifier, identifier));
47             }
48         }
49
50         internal static ValidationError ValidateIdentifier(string propName, IServiceProvider context, string identifier)
51         {
52             if (context == null)
53                 throw new ArgumentNullException("context");
54
55             ValidationError error = null;
56             if (identifier == null || identifier.Length == 0)
57                 error = new ValidationError(SR.GetString(SR.Error_PropertyNotSet, propName), ErrorNumbers.Error_PropertyNotSet);
58             else
59             {
60                 try
61                 {
62                     ValidationHelpers.ValidateIdentifier(context, identifier);
63                 }
64                 catch (Exception e)
65                 {
66                     error = new ValidationError(SR.GetString(SR.Error_InvalidIdentifier, propName, e.Message), ErrorNumbers.Error_InvalidIdentifier);
67                 }
68             }
69             if (error != null)
70                 error.PropertyName = propName;
71             return error;
72         }
73
74         internal static ValidationError ValidateNameProperty(string propName, IServiceProvider context, string identifier)
75         {
76             if (context == null)
77                 throw new ArgumentNullException("context");
78
79             ValidationError error = null;
80             if (identifier == null || identifier.Length == 0)
81                 error = new ValidationError(SR.GetString(SR.Error_PropertyNotSet, propName), ErrorNumbers.Error_PropertyNotSet);
82             else
83             {
84                 SupportedLanguages language = CompilerHelpers.GetSupportedLanguage(context);
85                 CodeDomProvider provider = CompilerHelpers.GetCodeDomProvider(language);
86
87                 if (language == SupportedLanguages.CSharp && identifier.StartsWith("@", StringComparison.Ordinal) ||
88                     language == SupportedLanguages.VB && identifier.StartsWith("[", StringComparison.Ordinal) && identifier.EndsWith("]", StringComparison.Ordinal) ||
89                     !provider.IsValidIdentifier(provider.CreateEscapedIdentifier(identifier)))
90                 {
91                     error = new ValidationError(SR.GetString(SR.Error_InvalidIdentifier, propName, SR.GetString(SR.Error_InvalidLanguageIdentifier, identifier)), ErrorNumbers.Error_InvalidIdentifier);
92                 }
93             }
94             if (error != null)
95                 error.PropertyName = propName;
96             return error;
97         }
98
99         internal static ValidationErrorCollection ValidateUniqueIdentifiers(Activity rootActivity)
100         {
101             if (rootActivity == null)
102                 throw new ArgumentNullException("rootActivity");
103
104             Hashtable identifiers = new Hashtable();
105             ValidationErrorCollection validationErrors = new ValidationErrorCollection();
106             Queue activities = new Queue();
107             activities.Enqueue(rootActivity);
108             while (activities.Count > 0)
109             {
110                 Activity activity = (Activity)activities.Dequeue();
111                 if (activity.Enabled)
112                 {
113                     if (identifiers.ContainsKey(activity.QualifiedName))
114                     {
115                         ValidationError duplicateError = new ValidationError(SR.GetString(SR.Error_DuplicatedActivityID, activity.QualifiedName), ErrorNumbers.Error_DuplicatedActivityID);
116                         duplicateError.PropertyName = "Name";
117                         duplicateError.UserData[typeof(Activity)] = activity;
118                         validationErrors.Add(duplicateError);
119                     }
120                     else
121                         identifiers.Add(activity.QualifiedName, activity);
122
123                     if (activity is CompositeActivity && ((activity.Parent == null) || !Helpers.IsCustomActivity(activity as CompositeActivity)))
124                     {
125                         foreach (Activity child in Helpers.GetAllEnabledActivities((CompositeActivity)activity))
126                             activities.Enqueue(child);
127                     }
128                 }
129             }
130
131             return validationErrors;
132
133         }
134         #endregion
135
136         #region Validation for Activity Ref order
137
138         internal static bool IsActivitySourceInOrder(Activity request, Activity response)
139         {
140             if (request.Parent == null)
141                 return true;
142             List<Activity> responsePath = new List<Activity>();
143             responsePath.Add(response);
144             Activity responseParent = response is CompositeActivity ? (CompositeActivity)response : response.Parent;
145             while (responseParent != null)
146             {
147                 responsePath.Add(responseParent);
148                 responseParent = responseParent.Parent;
149             }
150
151             Activity requestChild = request;
152             CompositeActivity requestParent = request is CompositeActivity ? (CompositeActivity)request : request.Parent;
153             while (requestParent != null && !responsePath.Contains(requestParent))
154             {
155                 requestChild = requestParent;
156                 requestParent = requestParent.Parent;
157             }
158
159             if (requestParent == requestChild)
160                 return true;
161
162             bool incorrectOrder = false;
163             int index = (responsePath.IndexOf(requestParent) - 1);
164             index = (index < 0) ? 0 : index; //sometimes parent gets added to the collection twice which causes index to be -1
165             Activity responseChild = responsePath[index];
166
167             if (requestParent == null || Helpers.IsAlternateFlowActivity(requestChild) || Helpers.IsAlternateFlowActivity(responseChild))
168                 incorrectOrder = true;
169             else
170             {
171                 for (int i = 0; i < requestParent.EnabledActivities.Count; i++)
172                 {
173                     if (requestParent.EnabledActivities[i] == requestChild)
174                         break;
175                     else if (requestParent.EnabledActivities[i] == responseChild)
176                     {
177                         incorrectOrder = true;
178                         break;
179                     }
180                 }
181             }
182             return !incorrectOrder;
183         }
184
185         #endregion
186
187         internal static ValidationErrorCollection ValidateObject(ValidationManager manager, object obj)
188         {
189             ValidationErrorCollection errors = new ValidationErrorCollection();
190             if (obj == null)
191                 return errors;
192
193             Type objType = obj.GetType();
194             if (!objType.IsPrimitive && (objType != typeof(string)))
195             {
196                 bool removeValidatedObjectCollection = false;
197                 Dictionary<int, object> validatedObjects = manager.Context[typeof(Dictionary<int, object>)] as Dictionary<int, object>;
198                 if (validatedObjects == null)
199                 {
200                     validatedObjects = new Dictionary<int, object>();
201                     manager.Context.Push(validatedObjects);
202                     removeValidatedObjectCollection = true;
203                 }
204
205                 try
206                 {
207                     if (!validatedObjects.ContainsKey(obj.GetHashCode()))
208                     {
209                         validatedObjects.Add(obj.GetHashCode(), obj);
210                         try
211                         {
212                             Validator[] validators = manager.GetValidators(objType);
213                             foreach (Validator validator in validators)
214                                 errors.AddRange(validator.Validate(manager, obj));
215                         }
216                         finally
217                         {
218                             validatedObjects.Remove(obj.GetHashCode());
219                         }
220                     }
221                 }
222                 finally
223                 {
224                     if (removeValidatedObjectCollection)
225                         manager.Context.Pop();
226                 }
227             }
228
229             return errors;
230         }
231
232         internal static ValidationErrorCollection ValidateActivity(ValidationManager manager, Activity activity)
233         {
234             ValidationErrorCollection errors = ValidationHelpers.ValidateObject(manager, activity);
235
236             foreach (ValidationError error in errors)
237             {
238                 if (!error.UserData.Contains(typeof(Activity)))
239                     error.UserData[typeof(Activity)] = activity;
240             }
241             return errors;
242         }
243
244         internal static ValidationErrorCollection ValidateProperty(ValidationManager manager, Activity activity, object obj, PropertyValidationContext propertyValidationContext)
245         {
246             return ValidateProperty(manager, activity, obj, propertyValidationContext, null);
247         }
248
249         internal static ValidationErrorCollection ValidateProperty(ValidationManager manager, Activity activity, object obj, PropertyValidationContext propertyValidationContext, object extendedPropertyContext)
250         {
251             if (manager == null)
252                 throw new ArgumentNullException("manager");
253             if (obj == null)
254                 throw new ArgumentNullException("obj");
255             if (propertyValidationContext == null)
256                 throw new ArgumentNullException("propertyValidationContext");
257
258             ValidationErrorCollection errors = new ValidationErrorCollection();
259
260             manager.Context.Push(activity);
261             manager.Context.Push(propertyValidationContext);
262             if (extendedPropertyContext != null)
263                 manager.Context.Push(extendedPropertyContext);
264             try
265             {
266                 errors.AddRange(ValidationHelpers.ValidateObject(manager, obj));
267             }
268             finally
269             {
270                 manager.Context.Pop();
271                 manager.Context.Pop();
272                 if (extendedPropertyContext != null)
273                     manager.Context.Pop();
274             }
275
276             return errors;
277         }
278     }
279 }