2 // Copyright (c) Microsoft Corporation. All rights reserved.
5 namespace Microsoft.Activities.Presentation.Xaml
8 using System.Activities;
9 using System.Activities.Debugger;
10 using System.Activities.DynamicUpdate;
11 using System.Activities.Presentation.View;
12 using System.Activities.Presentation.ViewState;
13 using System.Collections.Generic;
14 using System.Reflection;
18 internal static class ViewStateXamlHelper
20 static readonly string ViewStateManager = WorkflowViewState.ViewStateManagerProperty.MemberName;
21 static readonly string IdRef = WorkflowViewState.IdRefProperty.MemberName;
22 static readonly MethodInfo GetViewStateManager = typeof(WorkflowViewState).GetMethod("GetViewStateManager");
23 static readonly MethodInfo SetViewStateManager = typeof(WorkflowViewState).GetMethod("SetViewStateManager");
24 static readonly MethodInfo GetIdRef = typeof(WorkflowViewState).GetMethod("GetIdRef");
25 static readonly MethodInfo SetIdRef = typeof(WorkflowViewState).GetMethod("SetIdRef");
26 static readonly List<string> SourceLocationNames = new List<string>
28 XamlDebuggerXmlReader.StartLineName.MemberName,
29 XamlDebuggerXmlReader.StartColumnName.MemberName,
30 XamlDebuggerXmlReader.EndLineName.MemberName,
31 XamlDebuggerXmlReader.EndColumnName.MemberName
34 // These are used to discover that we have found a DynamicUpdateInfo.OriginalDefintion or OriginalActivityBuilder
35 // attached property member. We have "hardcoded" the *MemberName" here because DynamicUpdateInfo has the
36 // AttachableMemberIdentifier properties marked as private. But the DynamicUpdateInfo class itself is public,
37 // as are the Get and Set methods.
38 static readonly string DynamicUpdateOriginalDefinitionMemberName = "OriginalDefinition";
39 static readonly MethodInfo GetOriginalDefinition = typeof(DynamicUpdateInfo).GetMethod("GetOriginalDefinition");
40 static readonly MethodInfo SetOriginalDefinition = typeof(DynamicUpdateInfo).GetMethod("SetOriginalDefinition");
42 static readonly string DynamicUpdateOriginalActivityBuilderMemberName = "OriginalActivityBuilder";
43 static readonly MethodInfo GetOriginalActivityBuilder = typeof(DynamicUpdateInfo).GetMethod("GetOriginalActivityBuilder");
44 static readonly MethodInfo SetOriginalActivityBuilder = typeof(DynamicUpdateInfo).GetMethod("SetOriginalActivityBuilder");
46 // This method collects view state attached properties and generates a Xaml node stream
47 // with all view state information appearing within the ViewStateManager node.
48 // It is called when workflow definition is being serialized to string.
49 // inputReader - Nodestream with view state information as attached properties on the activity nodes.
50 // The reader is positioned at the begining of the workflow definition.
51 // idManager - This component issues running sequence numbers for IdRef.
52 // Result - Node stream positioned at the begining of the workflow definition with a
53 // ViewStateManager node containing all view state information.
54 // Implementation logic:
55 // 1. Scan the input nodestream Objects for attached properties that need to be converted (VirtualizedContainerService.HintSize and WorkflowViewStateService.ViewState).
56 // 2. If the Object had a IdRef value then use it otherwise generate a new value.
57 // 3. Store idRef value and corresponding viewstate related attached property nodes (from step 1)
58 // in the viewStateInfo dictionary.
59 // 4. Use the viewStateInfo dictionary to generate ViewStateManager node which is then inserted
60 // into the end of output nodestream.
61 public static XamlReader ConvertAttachedPropertiesToViewState(XamlObjectReader inputReader, ViewStateIdManager idManager)
63 // Stack to track StartObject/GetObject and EndObject nodes.
64 Stack<Frame> stack = new Stack<Frame>();
66 XamlMember viewStateManager = new XamlMember(ViewStateManager, GetViewStateManager, SetViewStateManager, inputReader.SchemaContext);
67 XamlMember idRefMember = new XamlMember(IdRef, GetIdRef, SetIdRef, inputReader.SchemaContext);
69 // Xaml member corresponding to x:Class property of the workflow definition. Used to find x:Class value in the node stream.
70 XamlMember activityBuilderName = new XamlMember(typeof(ActivityBuilder).GetProperty("Name"), inputReader.SchemaContext);
71 string activityBuilderTypeName = typeof(ActivityBuilder).Name;
73 // Dictionary to keep track of IdRefs and corresponding viewstate related
74 // attached property nodes.
75 Dictionary<string, XamlNodeList> viewStateInfo = new Dictionary<string, XamlNodeList>();
78 XamlNodeList workflowDefinition = new XamlNodeList(inputReader.SchemaContext);
80 using (XamlWriter workflowDefinitionWriter = workflowDefinition.Writer)
82 bool design2010NamespaceFound = false;
83 bool inIdRefMember = false;
84 bool inxClassMember = false;
85 bool skipWritingWorkflowDefinition = false;
86 bool skipReadingWorkflowDefinition = false;
87 string xClassName = null;
89 while (skipReadingWorkflowDefinition || inputReader.Read())
91 skipWritingWorkflowDefinition = false;
92 skipReadingWorkflowDefinition = false;
93 switch (inputReader.NodeType)
95 case XamlNodeType.NamespaceDeclaration:
96 if (inputReader.Namespace.Namespace.Equals(NameSpaces.Design2010, StringComparison.Ordinal))
98 design2010NamespaceFound = true;
102 case XamlNodeType.StartObject:
103 // Save the Xaml type and clr object on the stack frame. These are used later to generate
104 // IdRef values and attaching the same to the clr object.
105 stack.Push(new Frame() { Type = inputReader.Type, InstanceObject = inputReader.Instance });
107 // If the design2010 namespace was not found add the namespace node
108 // before the start object is written out.
109 if (!design2010NamespaceFound)
111 workflowDefinitionWriter.WriteNamespace(new NamespaceDeclaration(NameSpaces.Design2010, NameSpaces.Design2010Prefix));
112 design2010NamespaceFound = true;
116 case XamlNodeType.GetObject:
117 // Push an empty frame to balance the Pop operation when the EndObject node
119 stack.Push(new Frame() { Type = null });
122 case XamlNodeType.StartMember:
123 // Track when we enter IdRef member so that we can save its value.
124 if (inputReader.Member.Equals(idRefMember))
126 inIdRefMember = true;
128 // Track when we enter x:Class member so that we can save its value.
129 else if (inputReader.Member.Equals(activityBuilderName))
131 inxClassMember = true;
133 // Start of VirtualizedContainerService.HintSize or WorkflowViewStateService.ViewState property.
134 else if (IsAttachablePropertyForConvert(inputReader))
136 // The top of stack here corresponds to the activity on which
137 // the above properties are attached.
138 if (stack.Peek().AttachedPropertyNodes == null)
140 stack.Peek().AttachedPropertyNodes = new XamlNodeList(inputReader.SchemaContext);
143 // Write the attached property's xaml nodes into the stack.
144 XamlReader subTreeReader = inputReader.ReadSubtree();
145 XamlWriter attachedPropertyWriter = stack.Peek().AttachedPropertyNodes.Writer;
146 while (subTreeReader.Read())
148 attachedPropertyWriter.WriteNode(subTreeReader);
151 // The subtree reader loop put us at the begining of the next node in the input stream.
152 // So skip reading/writing it out just yet.
153 skipReadingWorkflowDefinition = true;
154 skipWritingWorkflowDefinition = true;
158 case XamlNodeType.Value:
159 // Read and save IdRef/x:Class member values.
160 // Also update idManager to keep track of prefixes and ids seen.
163 string idRef = inputReader.Value as string;
164 stack.Peek().IdRef = idRef;
165 idManager.UpdateMap(idRef);
167 else if (inxClassMember)
169 xClassName = inputReader.Value as string;
170 idManager.UpdateMap(xClassName);
174 case XamlNodeType.EndMember:
175 // Exit IdRef/x:Class member state.
178 inIdRefMember = false;
180 else if (inxClassMember)
182 inxClassMember = false;
186 case XamlNodeType.EndObject:
187 // Remove an item from the stack because we encountered the end of an object definition.
188 Frame frameObject = stack.Pop();
190 // If the object had (viewstate related) attached properties we need to save them
191 // into the viewStateInfo dictionary.
192 if (frameObject.AttachedPropertyNodes != null)
194 frameObject.AttachedPropertyNodes.Writer.Close();
196 // If the object didn't have IdRef, generate a new one.
197 if (string.IsNullOrWhiteSpace(frameObject.IdRef))
199 // Use the object type name (or x:Class value) to generate a new id.
200 if (frameObject.Type != null)
202 string prefix = frameObject.Type.Name;
203 if (frameObject.Type.UnderlyingType != null)
205 prefix = frameObject.Type.UnderlyingType.Name;
208 if (string.CompareOrdinal(prefix, activityBuilderTypeName) == 0 && !string.IsNullOrWhiteSpace(xClassName))
210 frameObject.IdRef = idManager.GetNewId(xClassName);
214 frameObject.IdRef = idManager.GetNewId(prefix);
217 else //Fallback to generating a guid value.
219 frameObject.IdRef = Guid.NewGuid().ToString();
222 // Since we didn't see a IdRef on this object, insert the generated
223 // viewstate id into the output Xaml node-stream.
224 workflowDefinitionWriter.WriteStartMember(idRefMember);
225 workflowDefinitionWriter.WriteValue(frameObject.IdRef);
226 workflowDefinitionWriter.WriteEndMember();
228 // Save the generated idRef on the corresponding clr object as well.
229 if (frameObject.InstanceObject != null)
231 WorkflowViewState.SetIdRef(frameObject.InstanceObject, frameObject.IdRef);
235 viewStateInfo[frameObject.IdRef] = frameObject.AttachedPropertyNodes;
238 // We're at the end of input nodestream and have collected data in viewStateInfo
239 // so we need to create and insert the ViewStateManager nodes into the output nodestream.
240 if (stack.Count == 0 && viewStateInfo.Count > 0)
242 XamlNodeList viewStateManagerNodeList = CreateViewStateManagerNodeList(viewStateInfo, inputReader.SchemaContext);
243 XamlReader viewStateManagerNodeReader = viewStateManagerNodeList.GetReader();
245 // Insert the ViewStateManager nodes into the output node stream.
246 workflowDefinitionWriter.WriteStartMember(viewStateManager);
247 while (viewStateManagerNodeReader.Read())
249 workflowDefinitionWriter.WriteNode(viewStateManagerNodeReader);
251 workflowDefinitionWriter.WriteEndMember(); // viewStateManager
256 if (!skipWritingWorkflowDefinition)
258 workflowDefinitionWriter.WriteNode(inputReader);
263 return workflowDefinition.GetReader();
266 // This method converts view state information stored within the ViewStateManager node back as
267 // attached properties on corresponding activity nodes.
268 // It is called when workflow definition is being deserialized from a string.
269 // inputReader - Nodestream that may have all view state information in the ViewStateManager node at the end of workflow definition.
270 // The reader is positioned at the begining of the workflow definition.
271 // idManager - This component issues running sequence numbers for IdRef.
272 // viewStateManager - (output) ViewStateManager object instance deserialized from the workflow definition.
273 // Result - Node stream positioned at the begining of the workflow definition with view state related information
274 // appearing as attached properties on activities. The ViewStateManager nodes are removed from the stream.
275 // Implementation logic:
276 // 1. Scan the input nodestream for ViewStateManager node.
277 // 2. If ViewStateManager node is found, store Id and corresponding attached property nodes
278 // in viewStateInfo dictionary. Otherwise return early.
279 // 3. Walk activity nodes in the workflow definition and apply viewstate related attached properties (from
280 // viewStateInfo dictionary) to each node.
281 // 4. If multiple activities have same IdRef values then corresponding viewstate related attached properties
282 // (from viewStateInfo dictionary) are applied to the first of those activities. The other activities with duplicate
283 // IdRef values do not get view state information.
284 public static XamlReader ConvertViewStateToAttachedProperties(XamlReader inputReader, ViewStateIdManager idManager, out Dictionary<string, SourceLocation> viewStateSourceLocationMap)
286 int idRefLineNumber = 0;
287 int idRefLinePosition = 0;
288 bool shouldWriteIdRefEndMember = false;
290 XamlReader retVal = null;
292 // Xaml member definition for IdRef. Used to identify existing IdRef properties in the input nodestream.
293 XamlMember idRefMember = new XamlMember(IdRef, GetIdRef, SetIdRef, inputReader.SchemaContext);
295 // These are used to ignore the IdRef members that are inside a DynamicUpdateInfo.OriginalDefinition/OriginalActivityBuilder attached property.
296 // We need to ignore these because if we don't, the IdRef values for the objects in the actual workflow defintion will be ignored because of the
297 // duplicate IdRef value. This causes problems with activity designers that depend on the ViewStateManager data to correctly display the workflow
298 // on the WorkflowDesigner canvas.
299 XamlMember originalDefinitionMember = new XamlMember(DynamicUpdateOriginalDefinitionMemberName, GetOriginalDefinition, SetOriginalDefinition, inputReader.SchemaContext);
300 XamlMember originalActivityBuilderMember = new XamlMember(DynamicUpdateOriginalActivityBuilderMemberName, GetOriginalActivityBuilder, SetOriginalActivityBuilder, inputReader.SchemaContext);
302 // insideOriginalDefintion gets set to true when we find a "StartMember" node for either of the above two attached properties.
303 // originalDefintionMemberCount gets incremented if we find any "StartMember" and insideOriginalDefinition is true.
304 // originalDefintionMemberCount gets decremented if we find any "EndMember" and insideOriginalDefintion is true.
305 // insideOriginalDefintion gets set to false when we find an "EndMember" and originalDefinitionMemberCount gets decremented to 0.
306 // If insideOriginalDefintion is true when we find an "IdRef" member, we do NOT add that IdRef to the idRefsSeen HashSet to avoid
307 // duplicates being defined by the IdRefs inside of the OriginalDefinition attached properties.
308 bool insideOriginalDefinition = false;
309 int originalDefinitionMemberCount = 0;
311 // Dictionary containing Ids and corresponding viewstate related
312 // attached property nodes. Populated by StripViewStateElement method.
313 Dictionary<string, XamlNodeList> viewStateInfo = null;
314 XamlReader workflowDefinition = StripViewStateElement(inputReader, out viewStateInfo, out viewStateSourceLocationMap);
316 // This is used to keep track of duplicate IdRefs in the workflow definition.
317 HashSet<string> idRefsSeen = new HashSet<string>();
319 // If the inputReader did not have a ViewStateManager node (4.0 format)
321 if (viewStateInfo == null)
323 retVal = workflowDefinition;
327 // Stack to track StartObject/GetObject and EndObject nodes.
328 Stack<Frame> stack = new Stack<Frame>();
331 XamlNodeList mergedNodeList = new XamlNodeList(workflowDefinition.SchemaContext);
332 bool inIdRefMember = false;
334 using (XamlWriter mergedNodeWriter = mergedNodeList.Writer)
336 IXamlLineInfo lineInfo = workflowDefinition as IXamlLineInfo;
337 IXamlLineInfoConsumer lineInfoComsumer = mergedNodeWriter as IXamlLineInfoConsumer;
338 bool shouldPassLineInfo = lineInfo != null && lineInfo.HasLineInfo && lineInfoComsumer != null && lineInfoComsumer.ShouldProvideLineInfo;
340 while (workflowDefinition.Read())
342 bool skipWritingWorkflowDefinition = false;
344 switch (workflowDefinition.NodeType)
346 case XamlNodeType.StartObject:
347 stack.Push(new Frame { Type = workflowDefinition.Type });
350 case XamlNodeType.GetObject:
351 stack.Push(new Frame { Type = null });
354 case XamlNodeType.StartMember:
355 // If we find a StartMember for DynamicUpdateInfo.OriginalDefinition or OriginalActivityBuilder, remember that we are
356 // inside one of those. We don't want to "remember" IdRef values in the idRefsSeen HashSet while inside these attached properties.
357 if (workflowDefinition.Member.Equals(originalDefinitionMember) || workflowDefinition.Member.Equals(originalActivityBuilderMember))
359 insideOriginalDefinition = true;
362 if (insideOriginalDefinition)
364 originalDefinitionMemberCount++;
367 // Track when the reader enters IdRef. Skip writing the start
368 // node to the output nodelist until we check for duplicates.
369 else if (workflowDefinition.Member.Equals(idRefMember))
371 inIdRefMember = true;
372 skipWritingWorkflowDefinition = true;
374 if (shouldPassLineInfo)
376 idRefLineNumber = lineInfo.LineNumber;
377 idRefLinePosition = lineInfo.LinePosition;
382 case XamlNodeType.Value:
385 // We don't want to deal with the IdRef if we are inside a DynamicUpdateInfo.OriginalDefinition/OriginalActivityBuilder
386 // attached property.
387 if (!insideOriginalDefinition)
389 string idRef = workflowDefinition.Value as string;
390 if (!string.IsNullOrWhiteSpace(idRef))
392 // If IdRef value is a duplicate then do not associate it with
393 // the stack frame (top of stack == activity node with IdRef member on it).
394 if (idRefsSeen.Contains(idRef))
396 stack.Peek().IdRef = null;
398 // If the IdRef value is unique then associate it with the
399 // stack frame and also write its value into the output nodestream.
402 stack.Peek().IdRef = idRef;
403 idManager.UpdateMap(idRef);
404 idRefsSeen.Add(idRef);
406 if (shouldPassLineInfo)
408 lineInfoComsumer.SetLineInfo(idRefLineNumber, idRefLinePosition);
411 mergedNodeWriter.WriteStartMember(idRefMember);
413 if (shouldPassLineInfo)
415 lineInfoComsumer.SetLineInfo(lineInfo.LineNumber, lineInfo.LinePosition);
418 mergedNodeWriter.WriteValue(idRef);
420 shouldWriteIdRefEndMember = true;
424 // Don't need to write IdRef value into the output
425 // nodestream. If the value was valid, it would have been written above.
426 skipWritingWorkflowDefinition = true;
430 case XamlNodeType.EndMember:
431 // If we are inside an OriginalDefinition/OriginalActivityBuilder attached property,
432 // decrement the count and if it goes to zero, set insideOriginalDefintion to false
433 // because we just encountered the EndMember for it.
434 if (insideOriginalDefinition)
436 originalDefinitionMemberCount--;
437 if (originalDefinitionMemberCount == 0)
439 insideOriginalDefinition = false;
443 // Exit IdRef node. Skip writing the EndMember node, we would have done
444 // it as part of reading the IdRef value.
445 if (inIdRefMember && !insideOriginalDefinition)
447 inIdRefMember = false;
448 skipWritingWorkflowDefinition = true;
450 if (shouldWriteIdRefEndMember)
452 shouldWriteIdRefEndMember = false;
454 if (shouldPassLineInfo)
456 lineInfoComsumer.SetLineInfo(lineInfo.LineNumber, lineInfo.LinePosition);
459 mergedNodeWriter.WriteEndMember();
465 case XamlNodeType.EndObject:
466 Frame frameObject = stack.Pop();
467 // Before we exit the end of an object, check if it had IdRef
468 // associated with it. If it did, look-up viewStateInfo for viewstate
469 // related attached property nodes and add them to the output nodelist.
470 if (!string.IsNullOrWhiteSpace(frameObject.IdRef))
472 XamlNodeList viewStateNodeList;
473 if (viewStateInfo.TryGetValue(frameObject.IdRef, out viewStateNodeList))
475 XamlReader viewStateReader = viewStateNodeList.GetReader();
477 IXamlLineInfo viewStateLineInfo = viewStateReader as IXamlLineInfo;
478 bool viewStateShouldPassLineInfo = viewStateLineInfo != null && viewStateLineInfo.HasLineInfo && lineInfoComsumer != null && lineInfoComsumer.ShouldProvideLineInfo;
480 while (viewStateReader.Read())
482 if (viewStateShouldPassLineInfo)
484 lineInfoComsumer.SetLineInfo(viewStateLineInfo.LineNumber, viewStateLineInfo.LinePosition);
487 mergedNodeWriter.WriteNode(viewStateReader);
493 if (!skipWritingWorkflowDefinition)
495 if (shouldPassLineInfo)
497 lineInfoComsumer.SetLineInfo(lineInfo.LineNumber, lineInfo.LinePosition);
500 mergedNodeWriter.WriteNode(workflowDefinition);
505 retVal = mergedNodeList.GetReader();
511 // This method removes IdRef nodes from the nodestream. This method would be called
512 // when a 4.5 workflow definition is retargeted to 4.0.
513 public static XamlReader RemoveIdRefs(XamlObjectReader inputReader)
515 XamlMember idRefMember = new XamlMember(IdRef, GetIdRef, SetIdRef, inputReader.SchemaContext);
517 XamlNodeList outputNodeList = new XamlNodeList(inputReader.SchemaContext);
518 using (XamlWriter outputWriter = outputNodeList.Writer)
520 while (inputReader.Read())
522 if (inputReader.NodeType == XamlNodeType.StartMember && inputReader.Member.Equals(idRefMember))
524 // Exhaust the idRefMember sub-tree.
525 XamlReader idRefReader = inputReader.ReadSubtree();
526 while (idRefReader.Read());
528 outputWriter.WriteNode(inputReader);
531 return outputNodeList.GetReader();
534 // This is a helper method to output the nodestream sequence for debugging/diagnostic purposes.
535 public static void NodeLoopTest(XamlReader xamlReader)
540 while (xamlReader.Read())
542 switch (xamlReader.NodeType)
544 case XamlNodeType.NamespaceDeclaration:
545 System.Diagnostics.Debug.WriteLine(tabs + "Namespace declaration: {0}:{1}", xamlReader.Namespace.Prefix, xamlReader.Namespace.Namespace);
547 case XamlNodeType.StartObject:
548 tabs = new String(' ', depth++);
549 System.Diagnostics.Debug.WriteLine(tabs + "Start object: {0}", xamlReader.Type.Name);
551 case XamlNodeType.GetObject:
552 tabs = new String(' ', depth++);
553 System.Diagnostics.Debug.WriteLine(tabs + "Get object");
555 case XamlNodeType.StartMember:
556 tabs = new String(' ', depth++);
557 System.Diagnostics.Debug.WriteLine(tabs + "Start member: {0}, Attachable: {1}", xamlReader.Member.Name, xamlReader.Member.IsAttachable);
559 case XamlNodeType.Value:
560 tabs = new String(' ', depth++);
561 System.Diagnostics.Debug.WriteLine(tabs + "Value: {0}", xamlReader.Value);
564 case XamlNodeType.EndMember:
565 tabs = new String(' ', --depth);
566 System.Diagnostics.Debug.WriteLine(tabs + "End member");
568 case XamlNodeType.EndObject:
569 tabs = new String(' ', --depth);
570 System.Diagnostics.Debug.WriteLine(tabs + "End object");
577 // Given the viewStateInfo dictionary, this method returns a xaml node list matching a ViewStateManager
579 static XamlNodeList CreateViewStateManagerNodeList(Dictionary<string, XamlNodeList> viewStateInfo, XamlSchemaContext schemaContext)
581 XamlNodeList viewStateManagerNodeList = new XamlNodeList(schemaContext);
583 XamlMember viewStateDataMember = new XamlMember(typeof(ViewStateManager).GetProperty("ViewStateData"), schemaContext);
584 XamlType viewStateManagerType = new XamlType(typeof(ViewStateManager), schemaContext);
585 XamlType viewStateDataType = new XamlType(typeof(ViewStateData), schemaContext);
586 XamlMember idMember = new XamlMember(typeof(ViewStateData).GetProperty("Id"), schemaContext);
588 using (XamlWriter viewStateManagerNodeWriter = viewStateManagerNodeList.Writer)
590 viewStateManagerNodeWriter.WriteStartObject(viewStateManagerType);
591 viewStateManagerNodeWriter.WriteStartMember(viewStateDataMember);
592 viewStateManagerNodeWriter.WriteGetObject();
593 viewStateManagerNodeWriter.WriteStartMember(XamlLanguage.Items);
595 foreach (KeyValuePair<string, XamlNodeList> entry in viewStateInfo)
597 viewStateManagerNodeWriter.WriteStartObject(viewStateDataType);
599 viewStateManagerNodeWriter.WriteStartMember(idMember);
600 viewStateManagerNodeWriter.WriteValue(entry.Key);
601 viewStateManagerNodeWriter.WriteEndMember(); // idMember
603 XamlReader viewStateValueReader = entry.Value.GetReader();
604 while (viewStateValueReader.Read())
606 viewStateManagerNodeWriter.WriteNode(viewStateValueReader);
609 viewStateManagerNodeWriter.WriteEndObject(); // viewStateDataType
612 viewStateManagerNodeWriter.WriteEndMember(); // XamlLanguage.Items
613 viewStateManagerNodeWriter.WriteEndObject(); // GetObject
614 viewStateManagerNodeWriter.WriteEndMember(); // viewStateDataMember
615 viewStateManagerNodeWriter.WriteEndObject(); // viewStateManagerType
616 viewStateManagerNodeWriter.Close();
619 return viewStateManagerNodeList;
622 // Checks if the Xaml reader is on one of WorkflowViewStateService.ViewState or
623 // VirtualizedContainerService.HintSize members in the nodestream. These
624 // members need to be moved into the ViewStateManager node during the conversion.
625 static bool IsAttachablePropertyForConvert(XamlReader reader)
627 if (reader.NodeType == XamlNodeType.StartMember)
629 XamlMember member = reader.Member;
630 if (member.IsAttachable)
632 if (member.DeclaringType.UnderlyingType == typeof(WorkflowViewStateService) && member.Name.Equals("ViewState", StringComparison.Ordinal))
636 else if (member.DeclaringType.UnderlyingType == typeof(VirtualizedContainerService) && member.Name.Equals("HintSize", StringComparison.Ordinal))
645 // This method reads ViewStateManager nodes from the xaml nodestream and outputs that in the
646 // viewStateInfo dictionary. The input reader is positioned at the begining of the workflow definition.
647 // The method returns a reader positioned at the begining of the workflow definition with the ViewStateManager
649 static XamlReader StripViewStateElement(XamlReader inputReader, out Dictionary<string, XamlNodeList> viewStateInfo, out Dictionary<string, SourceLocation> viewStateSourceLocationMap)
651 viewStateSourceLocationMap = null;
652 XamlNodeList strippedNodeList = new XamlNodeList(inputReader.SchemaContext);
653 XamlMember viewStateManager = new XamlMember(ViewStateManager, GetViewStateManager, SetViewStateManager, inputReader.SchemaContext);
655 using (XamlWriter strippedWriter = strippedNodeList.Writer)
657 IXamlLineInfo lineInfo = inputReader as IXamlLineInfo;
658 IXamlLineInfoConsumer lineInfoComsumer = strippedWriter as IXamlLineInfoConsumer;
659 bool shouldPassLineInfo = lineInfo != null && lineInfo.HasLineInfo && lineInfoComsumer != null && lineInfoComsumer.ShouldProvideLineInfo;
661 viewStateInfo = null;
662 while (inputReader.Read())
664 if (inputReader.NodeType == XamlNodeType.StartMember && inputReader.Member.Equals(viewStateManager))
666 ReadViewStateInfo(inputReader.ReadSubtree(), out viewStateInfo, out viewStateSourceLocationMap);
670 if (shouldPassLineInfo)
672 lineInfoComsumer.SetLineInfo(lineInfo.LineNumber, lineInfo.LinePosition);
675 strippedWriter.WriteNode(inputReader);
679 return strippedNodeList.GetReader();
682 // This method reads ViewStateManager nodes from the xaml nodestream and outputs that in the
683 // viewStateInfo dictionary. The input reader is positioned on the ViewStateManagerNode in the nodestream.
684 static void ReadViewStateInfo(XamlReader inputReader, out Dictionary<string, XamlNodeList> viewStateInfo, out Dictionary<string, SourceLocation> viewStateSourceLocationMap)
686 XamlType viewStateType = new XamlType(typeof(ViewStateData), inputReader.SchemaContext);
688 viewStateInfo = new Dictionary<string, XamlNodeList>();
689 viewStateSourceLocationMap = new Dictionary<string, SourceLocation>();
690 bool skipReading = false;
691 while (skipReading || inputReader.Read())
694 if (inputReader.NodeType == XamlNodeType.StartObject && inputReader.Type.Equals(viewStateType))
697 XamlNodeList viewStateNodeList;
698 SourceLocation viewStateSourceLocation = null;
699 ReadViewState(viewStateType, inputReader.ReadSubtree(), out id, out viewStateNodeList, out viewStateSourceLocation);
702 viewStateInfo[id] = viewStateNodeList;
703 viewStateSourceLocationMap[id] = viewStateSourceLocation;
706 //inputReader will be positioned on the next node so no need to advance it.
712 // This method reads a ViewStateData node from the xaml nodestream. It outputs the Id property into viewStateId
713 // and the attached viewstate related properties in viewStateNodes. The input reader is positioned on a
714 // ViewStateData node within ViewStateManager.
715 static void ReadViewState(XamlType viewStateType, XamlReader xamlReader, out string viewStateId, out XamlNodeList viewStateNodes, out SourceLocation sourceLocation)
717 int globalMemberLevel = 0;
718 bool skippingUnexpectedAttachedProperty = false;
719 int skippingUnexpectedAttachedPropertyLevel = 0;
721 viewStateNodes = new XamlNodeList(viewStateType.SchemaContext);
722 sourceLocation = null;
724 Stack<Frame> objectNodes = new Stack<Frame>();
725 XamlMember idMember = new XamlMember(typeof(ViewStateData).GetProperty("Id"), xamlReader.SchemaContext);
726 int[] viewStateDataSourceLocation = new int[4];
727 int sourceLocationIndex = -1;
729 IXamlLineInfo lineInfo = xamlReader as IXamlLineInfo;
730 IXamlLineInfoConsumer lineInfoComsumer = viewStateNodes.Writer as IXamlLineInfoConsumer;
731 bool shouldPassLineInfo = lineInfo != null && lineInfo.HasLineInfo && lineInfoComsumer != null && lineInfoComsumer.ShouldProvideLineInfo;
733 while (xamlReader.Read())
735 bool skipWritingToNodeList = false;
736 switch (xamlReader.NodeType)
738 case XamlNodeType.StartObject:
739 if (xamlReader.Type.Equals(viewStateType))
741 skipWritingToNodeList = true;
743 objectNodes.Push(new Frame { Type = xamlReader.Type });
746 case XamlNodeType.GetObject:
747 objectNodes.Push(new Frame { Type = null });
750 case XamlNodeType.StartMember:
752 if (xamlReader.Member.Equals(idMember))
754 XamlReader idNode = xamlReader.ReadSubtree();
755 while (idNode.Read())
757 if (idNode.NodeType == XamlNodeType.Value)
759 viewStateId = idNode.Value as string;
763 // The xamlReader.ReadSubtree and subsequent while loop to get the Id member
764 // has moved the xamlReader forward to the next member. We need to check to see
765 // if it is an Attached Property that we care about. If it isn't then we need to
766 // skip it and not put it in the resulting XamlNodeList.
767 if (globalMemberLevel == 1 && !IsAttachablePropertyForConvert(xamlReader))
769 skippingUnexpectedAttachedProperty = true;
771 if (skippingUnexpectedAttachedProperty)
773 skippingUnexpectedAttachedPropertyLevel++;
776 sourceLocationIndex = GetViewStateDataSourceLocationIndexFromCurrentReader(xamlReader);
779 case XamlNodeType.EndMember:
781 if (skippingUnexpectedAttachedProperty)
783 skippingUnexpectedAttachedPropertyLevel--;
786 case XamlNodeType.Value:
787 if (xamlReader.Value is int
788 && sourceLocationIndex >= 0
789 && sourceLocationIndex < viewStateDataSourceLocation.Length)
791 viewStateDataSourceLocation[sourceLocationIndex] = (int)xamlReader.Value;
795 case XamlNodeType.EndObject:
796 Frame objectNode = objectNodes.Pop();
797 if (objectNode.Type != null && objectNode.Type.Equals(viewStateType))
799 skipWritingToNodeList = true;
800 // The ViewStateData's source location should be valid, because
801 // before each EndObject, its SourceLocation is injected.
802 // If not, an exception will be thrown from constructor
803 // of SourceLocation.
804 sourceLocation = new SourceLocation(null,
805 viewStateDataSourceLocation[0],
806 viewStateDataSourceLocation[1],
807 viewStateDataSourceLocation[2],
808 viewStateDataSourceLocation[3]
812 Array.Clear(viewStateDataSourceLocation, 0, viewStateDataSourceLocation.Length);
816 if (!skipWritingToNodeList && !skippingUnexpectedAttachedProperty)
818 if (shouldPassLineInfo)
820 lineInfoComsumer.SetLineInfo(lineInfo.LineNumber, lineInfo.LinePosition);
823 viewStateNodes.Writer.WriteNode(xamlReader);
826 if (skippingUnexpectedAttachedPropertyLevel == 0)
828 skippingUnexpectedAttachedProperty = false;
831 viewStateNodes.Writer.Close();
834 private static int GetViewStateDataSourceLocationIndexFromCurrentReader(XamlReader xamlReader)
836 if (xamlReader.NodeType != XamlNodeType.StartMember
837 || xamlReader.Member == null
838 || xamlReader.Member.UnderlyingMember == null
839 || xamlReader.Member.UnderlyingMember.DeclaringType != typeof(XamlDebuggerXmlReader))
844 // if UnderlineType is XamlDebuggerXmlReader, see if it
845 // is one of {StartLine, StartColumn, EndLine, EndColumn}
846 return SourceLocationNames.IndexOf(xamlReader.Member.Name);
849 // This class is used for tracking Xaml nodestream data.
852 // IdRef value if any associated with the node.
853 public string IdRef { get; set; }
855 // XamlType of the node. Helps generating IdRef values.
856 public XamlType Type { get; set; }
858 // XamlNodes corresponding to viewstate related attached property nodes.
859 public XamlNodeList AttachedPropertyNodes { get; set; }
861 // Underlying CLR object. Used to attach generated IdRef values
862 // when serializing workflow definition.
863 public object InstanceObject { get; set; }