1 //----------------------------------------------------------------
2 // Copyright (c) Microsoft Corporation.  All rights reserved.
3 //----------------------------------------------------------------
4 
5 namespace System.Activities.XamlIntegration
6 {
7     using System;
8     using System.Collections.Generic;
9     using System.Runtime;
10     using System.Xaml;
11     using System.Xaml.Schema;
12 
13     // This Xaml Reader converts an <Activity x:Class=Foo to <DynamicActivity Name=Foo
14     // it does the folowing
15     // Rewrites any record of type "Activity" to "DynamicActivity"
16     // Rewrites any member of type "Activity" to member "DynamicActivity"
17     // Rewrites x:Class to DynamicActivity.Name
18     // Recognizes DynamicActivity<T>.
19     //
20     // This Xaml Reader also supports ActivityBuilder, which has the same basic node structure
21     class DynamicActivityXamlReader : XamlReader, IXamlLineInfo
22     {
23         internal static readonly XamlMember xPropertyType = XamlLanguage.Property.GetMember("Type");
24         internal static readonly XamlMember xPropertyName = XamlLanguage.Property.GetMember("Name");
25         internal static readonly XamlMember xPropertyAttributes = XamlLanguage.Property.GetMember("Attributes");
26 
27         // These may be a closed generic types in the Activity<T> case, so we compute them dynamically
28         XamlType activityReplacementXamlType;
29         XamlType activityXamlType;
30 
31         readonly XamlType baseActivityXamlType;
32         readonly XamlType activityPropertyXamlType;
33         readonly XamlType xamlTypeXamlType;
34         readonly XamlType typeXamlType;
35 
36         readonly XamlMember activityPropertyType;
37         readonly XamlMember activityPropertyName;
38         readonly XamlMember activityPropertyAttributes;
39         readonly XamlMember activityPropertyValue;
40 
41         readonly XamlReader innerReader;
42         readonly NamespaceTable namespaceTable;
43 
44         const string clrNamespacePart = "clr-namespace:";
45 
46         int depth;
47         bool notRewriting;
48 
49         int inXClassDepth;
50 
51         XamlTypeName xClassName;
52         IXamlLineInfo nodeReaderLineInfo;
53         IXamlLineInfo innerReaderLineInfo;
54 
55         bool frontLoadedDirectives;
56         XamlSchemaContext schemaContext;
57         bool isBuilder;
58         bool hasLineInfo;
59 
60         // we pull off of the innerReader and into this nodeList, where we use its reader
61         XamlNodeQueue nodeQueue;
62         XamlReader nodeReader;
63 
64         // Properties are tricky since they support default values, and those values
65         // can appear anywhere in the XAML document. So we need to buffer their XAML
66         // nodes and present them only at the end of the document (right before the
67         // document end tag), when we have both the declaration and the value realized.
68         BufferedPropertyList bufferedProperties;
69 
70         // in the ActivityBuilder case we need to jump through some extra hoops to
71         // support PropertyReferenceExtension, since in the ActivityBuilder case
72         // Implementation isn't a template (Func<Activity>), so we need to map
73         // such members into attached properties on their parent object
74         BuilderStack builderStack;
75 
DynamicActivityXamlReader(XamlReader innerReader)76         public DynamicActivityXamlReader(XamlReader innerReader)
77             : this(innerReader, null)
78         {
79         }
80 
DynamicActivityXamlReader(XamlReader innerReader, XamlSchemaContext schemaContext)81         public DynamicActivityXamlReader(XamlReader innerReader, XamlSchemaContext schemaContext)
82             : this(false, innerReader, schemaContext)
83         {
84         }
85 
DynamicActivityXamlReader(bool isBuilder, XamlReader innerReader, XamlSchemaContext schemaContext)86         public DynamicActivityXamlReader(bool isBuilder, XamlReader innerReader, XamlSchemaContext schemaContext)
87             : base()
88         {
89             this.isBuilder = isBuilder;
90             this.innerReader = innerReader;
91             this.schemaContext = schemaContext ?? innerReader.SchemaContext;
92 
93             this.xamlTypeXamlType = this.schemaContext.GetXamlType(typeof(XamlType));
94             this.typeXamlType = this.schemaContext.GetXamlType(typeof(Type));
95 
96             this.baseActivityXamlType = this.schemaContext.GetXamlType(typeof(Activity));
97             this.activityPropertyXamlType = this.schemaContext.GetXamlType(typeof(DynamicActivityProperty));
98             this.activityPropertyType = this.activityPropertyXamlType.GetMember("Type");
99             this.activityPropertyName = this.activityPropertyXamlType.GetMember("Name");
100             this.activityPropertyValue = this.activityPropertyXamlType.GetMember("Value");
101             this.activityPropertyAttributes = this.activityPropertyXamlType.GetMember("Attributes");
102 
103             this.namespaceTable = new NamespaceTable();
104             this.frontLoadedDirectives = true;
105 
106             // we pump items through this node-list when rewriting
107             this.nodeQueue = new XamlNodeQueue(this.schemaContext);
108             this.nodeReader = this.nodeQueue.Reader;
109             IXamlLineInfo lineInfo = innerReader as IXamlLineInfo;
110             if (lineInfo != null && lineInfo.HasLineInfo)
111             {
112                 this.innerReaderLineInfo = lineInfo;
113                 this.nodeReaderLineInfo = (IXamlLineInfo)nodeQueue.Reader;
114                 this.hasLineInfo = true;
115             }
116         }
117 
118         public override XamlType Type
119         {
120             get
121             {
122                 return this.nodeReader.Type;
123             }
124         }
125 
126         public override NamespaceDeclaration Namespace
127         {
128             get
129             {
130                 return this.nodeReader.Namespace;
131             }
132         }
133 
134         public override object Value
135         {
136             get
137             {
138                 return this.nodeReader.Value;
139             }
140         }
141 
142         public override bool IsEof
143         {
144             get
145             {
146                 return this.nodeReader.IsEof;
147             }
148         }
149 
150         public override XamlMember Member
151         {
152             get
153             {
154                 return this.nodeReader.Member;
155             }
156         }
157 
158         public override XamlSchemaContext SchemaContext
159         {
160             get
161             {
162                 return this.schemaContext;
163             }
164         }
165 
166         public override XamlNodeType NodeType
167         {
168             get
169             {
170                 return this.nodeReader.NodeType;
171             }
172         }
173 
174         public bool HasLineInfo
175         {
176             get
177             {
178                 return this.hasLineInfo;
179             }
180         }
181 
182         public int LineNumber
183         {
184             get
185             {
186                 if (this.hasLineInfo)
187                 {
188                     return this.nodeReaderLineInfo.LineNumber;
189                 }
190                 else
191                 {
192                     return 0;
193                 }
194             }
195         }
196 
197         public int LinePosition
198         {
199             get
200             {
201                 if (this.hasLineInfo)
202                 {
203                     return this.nodeReaderLineInfo.LinePosition;
204                 }
205                 else
206                 {
207                     return 0;
208                 }
209             }
210         }
211 
Dispose(bool disposing)212         protected override void Dispose(bool disposing)
213         {
214             try
215             {
216                 if (disposing)
217                 {
218                     this.innerReader.Close();
219                 }
220             }
221             finally
222             {
223                 base.Dispose(disposing);
224             }
225         }
226 
CreateXamlException(string message, IXamlLineInfo lineInfo)227         static XamlException CreateXamlException(string message, IXamlLineInfo lineInfo)
228         {
229             if (lineInfo != null && lineInfo.HasLineInfo)
230             {
231                 return new XamlException(message, null, lineInfo.LineNumber, lineInfo.LinePosition);
232             }
233             else
234             {
235                 return new XamlException(message);
236             }
237         }
238 
239         // perf optimization to efficiently support non-Activity types
DisableRewrite()240         void DisableRewrite()
241         {
242             this.notRewriting = true;
243             this.nodeReader = this.innerReader;
244             this.nodeReaderLineInfo = this.innerReader as IXamlLineInfo;
245         }
246 
Read()247         public override bool Read()
248         {
249             if (this.notRewriting)
250             {
251                 Fx.Assert(object.ReferenceEquals(this.innerReader, this.nodeReader), "readers must match at this point");
252                 return this.nodeReader.Read();
253             }
254 
255             // for properties, we'll store nodes "on the side"
256             bool innerReaderResult = this.innerReader.Read();
257             bool continueProcessing = true;
258             while (continueProcessing && !this.innerReader.IsEof)
259             {
260                 // ProcessCurrentNode will only return true if it has advanced the innerReader
261                 continueProcessing = ProcessCurrentNode();
262             }
263 
264             // rewriting may have been disabled under ProcessCurrentNode
265             if (this.notRewriting)
266             {
267                 return innerReaderResult;
268             }
269             else
270             {
271                 // now that we've mapped the innerReader into (at least) one node entry, pump that reader as well
272                 return this.nodeReader.Read();
273             }
274         }
275 
276         // pull on our inner reader, map the results as necessary, and pump
277         // mapped results into the streaming node reader that we're offering up.
278         // return true if we need to keep pumping (because we've buffered some nodes on the side)
ProcessCurrentNode()279         bool ProcessCurrentNode()
280         {
281             bool processedNode = false;
282             this.namespaceTable.ManageNamespace(this.innerReader);
283 
284             switch (this.innerReader.NodeType)
285             {
286                 case XamlNodeType.StartMember:
287                     XamlMember currentMember = this.innerReader.Member;
288                     // find out if the member is a default value for one of
289                     // our declared properties. If it is, then we have a complex case
290                     // where we need to:
291                     // 1) read the nodes into a side list
292                     // 2) interleave these nodes with the DynamicActivityProperty nodes
293                     //    since they need to appear as DynamicActivityProperty.Value
294                     // 3) right before we hit the last node, we'll dump the side node-lists
295                     //    reflecting a zipped up representation of the Properties
296                     if (IsXClassName(currentMember.DeclaringType))
297                     {
298                         if (this.bufferedProperties == null)
299                         {
300                             this.bufferedProperties = new BufferedPropertyList(this);
301                         }
302                         this.bufferedProperties.BufferDefaultValue(currentMember.Name, this.activityPropertyValue, this.innerReader, this.innerReaderLineInfo);
303                         return true; // output cursor didn't move forward
304                     }
305                     else if (this.frontLoadedDirectives && currentMember == XamlLanguage.FactoryMethod)
306                     {
307                         DisableRewrite();
308                         return false;
309                     }
310                     else
311                     {
312                         this.depth++;
313                         if (this.depth == 2)
314                         {
315                             if (currentMember.DeclaringType == this.activityXamlType || currentMember.DeclaringType == this.baseActivityXamlType)
316                             {
317                                 // Rewrite "<Activity.XXX>" to "<DynamicActivity.XXX>"
318                                 XamlMember member = this.activityReplacementXamlType.GetMember(currentMember.Name);
319                                 if (member == null)
320                                 {
321                                     throw FxTrace.Exception.AsError(CreateXamlException(SR.MemberNotSupportedByActivityXamlServices(currentMember.Name), this.innerReaderLineInfo));
322                                 }
323 
324                                 this.nodeQueue.Writer.WriteStartMember(member, this.innerReaderLineInfo);
325 
326                                 if (member.Name == "Constraints")
327                                 {
328                                     WriteWrappedMember(true);
329                                     processedNode = true;
330                                     return true;
331                                 }
332 
333                                 processedNode = true;
334 
335                                 // if we're in ActivityBuilder.Implementation, start buffering nodes
336                                 if (this.isBuilder && member.Name == "Implementation")
337                                 {
338                                     this.builderStack = new BuilderStack(this);
339                                 }
340                             }
341                             else if (currentMember == XamlLanguage.Class)
342                             {
343                                 this.inXClassDepth = this.depth;
344 
345                                 // Rewrite x:Class to DynamicActivity.Name
346                                 this.nodeQueue.Writer.WriteStartMember(this.activityReplacementXamlType.GetMember("Name"), this.innerReaderLineInfo);
347                                 processedNode = true;
348                             }
349                             else if (currentMember == XamlLanguage.Members)
350                             {
351                                 // Rewrite "<x:Members>" to "<DynamicActivity.Properties>"
352                                 if (this.bufferedProperties == null)
353                                 {
354                                     this.bufferedProperties = new BufferedPropertyList(this);
355                                 }
356                                 this.bufferedProperties.BufferDefinitions(this);
357                                 this.depth--;
358                                 return true; // output cursor didn't move forward
359                             }
360                             else if (currentMember == XamlLanguage.ClassAttributes)
361                             {
362                                 // Rewrite x:ClassAttributes to DynamicActivity.Attributes
363                                 this.nodeQueue.Writer.WriteStartMember(this.activityReplacementXamlType.GetMember("Attributes"), this.innerReaderLineInfo);
364                                 // x:ClassAttributes directive has no following GetObject, but Attributes does since it's not a directive
365                                 WriteWrappedMember(false);
366                                 processedNode = true;
367                                 return true;
368                             }
369                         }
370                     }
371                     break;
372 
373                 case XamlNodeType.StartObject:
374                     {
375                         EnterObject();
376                         if (this.depth == 1)
377                         {
378                             // see if we're deserializing an Activity
379                             if (this.innerReader.Type.UnderlyingType == typeof(Activity))
380                             {
381                                 // Rewrite "<Activity>" to "<DynamicActivity>"
382                                 this.activityXamlType = this.innerReader.Type;
383                                 if (this.isBuilder)
384                                 {
385                                     this.activityReplacementXamlType = SchemaContext.GetXamlType(typeof(ActivityBuilder));
386                                 }
387                                 else
388                                 {
389                                     this.activityReplacementXamlType = SchemaContext.GetXamlType(typeof(DynamicActivity));
390                                 }
391                             }
392                             // or an Activity<TResult>
393                             else if (this.innerReader.Type.IsGeneric && this.innerReader.Type.UnderlyingType != null
394                                 && this.innerReader.Type.UnderlyingType.GetGenericTypeDefinition() == typeof(Activity<>))
395                             {
396                                 // Rewrite "<Activity typeArgument=T>" to "<DynamicActivity typeArgument=T>"
397                                 this.activityXamlType = this.innerReader.Type;
398 
399                                 Type activityType = this.innerReader.Type.TypeArguments[0].UnderlyingType;
400                                 Type activityReplacementGenericType;
401                                 if (this.isBuilder)
402                                 {
403                                     activityReplacementGenericType = typeof(ActivityBuilder<>).MakeGenericType(activityType);
404                                 }
405                                 else
406                                 {
407                                     activityReplacementGenericType = typeof(DynamicActivity<>).MakeGenericType(activityType);
408                                 }
409 
410                                 this.activityReplacementXamlType = SchemaContext.GetXamlType(activityReplacementGenericType);
411                             }
412                             // otherwise disable rewriting so that we're a pass through
413                             else
414                             {
415                                 DisableRewrite();
416                                 return false;
417                             }
418 
419                             this.nodeQueue.Writer.WriteStartObject(this.activityReplacementXamlType, this.innerReaderLineInfo);
420                             processedNode = true;
421                         }
422 
423                     }
424                     break;
425 
426                 case XamlNodeType.GetObject:
427                     EnterObject();
428                     break;
429 
430                 case XamlNodeType.EndObject:
431                 case XamlNodeType.EndMember:
432                     ExitObject();
433                     break;
434 
435                 case XamlNodeType.Value:
436                     if (this.inXClassDepth >= this.depth && this.xClassName == null)
437                     {
438                         string fullName = (string)this.innerReader.Value;
439                         string xClassNamespace = "";
440                         string xClassName = fullName;
441 
442                         int nameStartIndex = fullName.LastIndexOf('.');
443                         if (nameStartIndex > 0)
444                         {
445                             xClassNamespace = fullName.Substring(0, nameStartIndex);
446                             xClassName = fullName.Substring(nameStartIndex + 1);
447                         }
448 
449                         this.xClassName = new XamlTypeName(xClassNamespace, xClassName);
450                     }
451                     break;
452             }
453 
454             if (!processedNode)
455             {
456                 if (this.builderStack != null)
457                 {
458                     bool writeNode = true;
459                     this.builderStack.ProcessNode(this.innerReader, this.innerReaderLineInfo, this.nodeQueue.Writer, out writeNode);
460                     if (!writeNode)
461                     {
462                         this.innerReader.Read();
463                         return true;
464                     }
465                 }
466                 this.nodeQueue.Writer.WriteNode(this.innerReader, this.innerReaderLineInfo);
467             }
468             return false;
469         }
470 
471         // used for a number of cases when wrapping we need to add a GetObject/StartMember(_Items) since XAML directives intrinsically
472         // take care of it
WriteWrappedMember(bool stripWhitespace)473         void WriteWrappedMember(bool stripWhitespace)
474         {
475             this.nodeQueue.Writer.WriteGetObject(this.innerReaderLineInfo);
476             this.nodeQueue.Writer.WriteStartMember(XamlLanguage.Items, this.innerReaderLineInfo);
477             XamlReader subReader = this.innerReader.ReadSubtree();
478 
479             // 1) Read past the start member since we wrote it above
480             subReader.Read();
481 
482             // 2) copy over the rest of the subnodes, possibly discarding top-level whitespace from WhitespaceSignificantCollection
483             subReader.Read();
484             while (!subReader.IsEof)
485             {
486                 bool isWhitespaceNode = false;
487                 if (subReader.NodeType == XamlNodeType.Value)
488                 {
489                     string stringValue = subReader.Value as string;
490                     if (stringValue != null && stringValue.Trim().Length == 0)
491                     {
492                         isWhitespaceNode = true;
493                     }
494                 }
495 
496                 if (isWhitespaceNode && stripWhitespace)
497                 {
498                     subReader.Read();
499                 }
500                 else
501                 {
502                     XamlWriterExtensions.Transform(subReader.ReadSubtree(), this.nodeQueue.Writer, this.innerReaderLineInfo, false);
503                 }
504             }
505 
506 
507             // close the GetObject added above. Note that we are doing EndObject/EndMember after the last node (EndMember)
508             // rather than inserting EndMember/EndObject before the last EndMember since all EndMembers are interchangable from a state perspective
509             this.nodeQueue.Writer.WriteEndObject(this.innerReaderLineInfo);
510             this.nodeQueue.Writer.WriteEndMember(this.innerReaderLineInfo);
511 
512             subReader.Close();
513 
514             // we hand exited a member where we had increased the depth manually, so record that fact
515             ExitObject();
516         }
517 
518         // when Read hits StartObject or GetObject
EnterObject()519         void EnterObject()
520         {
521             this.depth++;
522             if (this.depth >= 2)
523             {
524                 this.frontLoadedDirectives = false;
525             }
526         }
527 
528         // when Read hits EndObject or EndMember
ExitObject()529         void ExitObject()
530         {
531             if (this.depth <= this.inXClassDepth)
532             {
533                 this.inXClassDepth = 0;
534             }
535 
536             this.depth--;
537             this.frontLoadedDirectives = false;
538 
539             if (this.depth == 1)
540             {
541                 this.builderStack = null;
542             }
543             else if (this.depth == 0)
544             {
545                 // we're about to write out the last tag. Dump our accrued properties
546                 // as no more property values are forthcoming.
547                 if (this.bufferedProperties != null)
548                 {
549                     this.bufferedProperties.FlushTo(this.nodeQueue, this);
550                 }
551             }
552         }
553 
IsXClassName(XamlType xamlType)554         bool IsXClassName(XamlType xamlType)
555         {
556             if (xamlType == null || this.xClassName == null || xamlType.Name != this.xClassName.Name)
557             {
558                 return false;
559             }
560 
561             // this code is kept for back compatible
562             string preferredNamespace = xamlType.PreferredXamlNamespace;
563             if (preferredNamespace.Contains(clrNamespacePart))
564             {
565                 return IsXClassName(preferredNamespace);
566             }
567 
568             // GetXamlNamespaces is a superset of PreferredXamlNamespace, it's not a must for the above code
569             // to check for preferredXamlNamespace, but since the old code uses .Contains(), which was a minor bug,
570             // we decide to use StartsWith in new code and keep the old code for back compatible reason.
571             IList<string> namespaces = xamlType.GetXamlNamespaces();
572             foreach (string ns in namespaces)
573             {
574                 if (ns.StartsWith(clrNamespacePart, StringComparison.Ordinal))
575                 {
576                     return IsXClassName(ns);
577                 }
578             }
579 
580             return false;
581         }
582 
IsXClassName(string ns)583         bool IsXClassName(string ns)
584         {
585             string clrNamespace = ns.Substring(clrNamespacePart.Length);
586 
587             int lastIndex = clrNamespace.IndexOf(';');
588             if (lastIndex < 0 || lastIndex > clrNamespace.Length)
589             {
590                 lastIndex = clrNamespace.Length;
591             }
592 
593             string @namespace = clrNamespace.Substring(0, lastIndex);
594             return this.xClassName.Namespace == @namespace;
595         }
596 
IncrementIfPositive(ref int a)597         static void IncrementIfPositive(ref int a)
598         {
599             if (a > 0)
600             {
601                 a++;
602             }
603         }
604 
DecrementIfPositive(ref int a)605         static void DecrementIfPositive(ref int a)
606         {
607             if (a > 0)
608             {
609                 a--;
610             }
611         }
612 
613         // This class tracks the information we need to be able to convert
614         // <PropertyReferenceExtension> into <ActivityBuilder.PropertyReferences>
615         class BuilderStack
616         {
617             readonly XamlType activityPropertyReferenceXamlType;
618             readonly XamlMember activityBuilderPropertyReferencesMember;
619             readonly XamlMember activityPropertyReferenceSourceProperty;
620             readonly XamlMember activityPropertyReferenceTargetProperty;
621 
622             MemberInformation bufferedMember;
623             DynamicActivityXamlReader parent;
624             Stack<Frame> stack;
625 
BuilderStack(DynamicActivityXamlReader parent)626             public BuilderStack(DynamicActivityXamlReader parent)
627             {
628                 this.parent = parent;
629                 this.stack = new Stack<Frame>();
630                 this.activityPropertyReferenceXamlType = parent.schemaContext.GetXamlType(typeof(ActivityPropertyReference));
631                 this.activityPropertyReferenceSourceProperty = this.activityPropertyReferenceXamlType.GetMember("SourceProperty");
632                 this.activityPropertyReferenceTargetProperty = this.activityPropertyReferenceXamlType.GetMember("TargetProperty");
633                 XamlType typeOfActivityBuilder = parent.schemaContext.GetXamlType(typeof(ActivityBuilder));
634                 this.activityBuilderPropertyReferencesMember = typeOfActivityBuilder.GetAttachableMember("PropertyReferences");
635             }
636 
ReadPropertyReferenceExtensionPropertyName(XamlReader reader)637             string ReadPropertyReferenceExtensionPropertyName(XamlReader reader)
638             {
639                 string sourceProperty = null;
640                 reader.Read();
641                 while (!reader.IsEof && reader.NodeType != XamlNodeType.EndObject)
642                 {
643                     if (IsExpectedPropertyReferenceMember(reader))
644                     {
645                         string propertyName = ReadPropertyName(reader);
646                         if (propertyName != null)
647                         {
648                             sourceProperty = propertyName;
649                         }
650                     }
651                     else
652                     {
653                         // unexpected members.
654                         // For compat with 4.0, unexpected members on PropertyReferenceExtension
655                         // are silently ignored
656                         reader.Skip();
657                     }
658                 }
659 
660                 return sourceProperty;
661             }
662 
663             // Whenever we encounter a StartMember, we buffer it (and any namespace nodes folllowing it)
664             // until we see its contents (SO/GO/V).
665             // If the content is a PropertyReferenceExtension, then we convert it to an ActivityPropertyReference
666             // in the parent object's ActivityBuilder.PropertyReference collection, and dont' write out the member.
667             // If the content is not a PropertyReferenceExtension, or there's no content (i.e. we hit an EM),
668             // we flush the buffered SM + NS*, and continue as normal.
ProcessNode(XamlReader reader, IXamlLineInfo lineInfo, XamlWriter targetWriter, out bool writeNodeToOutput)669             public void ProcessNode(XamlReader reader, IXamlLineInfo lineInfo, XamlWriter targetWriter, out bool writeNodeToOutput)
670             {
671                 writeNodeToOutput = true;
672 
673                 switch (reader.NodeType)
674                 {
675                     case XamlNodeType.StartMember:
676                         this.bufferedMember = new MemberInformation(reader.Member, lineInfo);
677                         writeNodeToOutput = false;
678                         break;
679 
680                     case XamlNodeType.EndMember:
681                         FlushBufferedMember(targetWriter);
682                         if (this.stack.Count > 0)
683                         {
684                             Frame curFrame = this.stack.Peek();
685                             if (curFrame.SuppressNextEndMember)
686                             {
687                                 writeNodeToOutput = false;
688                                 curFrame.SuppressNextEndMember = false;
689                             }
690                         }
691                         break;
692 
693                     case XamlNodeType.StartObject:
694                         Frame newFrame;
695                         if (IsPropertyReferenceExtension(reader.Type) && this.bufferedMember.IsSet)
696                         {
697                             MemberInformation targetMember = this.bufferedMember;
698                             this.bufferedMember = MemberInformation.None;
699                             WritePropertyReferenceFrameToParent(targetMember, ReadPropertyReferenceExtensionPropertyName(reader), this.stack.Peek(), lineInfo);
700                             writeNodeToOutput = false;
701                             break;
702                         }
703                         else
704                         {
705                             FlushBufferedMember(targetWriter);
706                             newFrame = new Frame();
707                         }
708                         this.stack.Push(newFrame);
709                         break;
710 
711                     case XamlNodeType.GetObject:
712                         FlushBufferedMember(targetWriter);
713                         this.stack.Push(new Frame());
714                         break;
715 
716                     case XamlNodeType.EndObject:
717                         Frame frame = this.stack.Pop();
718                         if (frame.PropertyReferences != null)
719                         {
720                             WritePropertyReferenceCollection(frame.PropertyReferences, targetWriter, lineInfo);
721                         }
722                         break;
723 
724                     case XamlNodeType.Value:
725                         FlushBufferedMember(targetWriter);
726                         break;
727 
728                     case XamlNodeType.NamespaceDeclaration:
729                         if (this.bufferedMember.IsSet)
730                         {
731                             if (this.bufferedMember.FollowingNamespaces == null)
732                             {
733                                 this.bufferedMember.FollowingNamespaces = new XamlNodeQueue(this.parent.schemaContext);
734                             }
735                             this.bufferedMember.FollowingNamespaces.Writer.WriteNode(reader, lineInfo);
736                             writeNodeToOutput = false;
737                         }
738                         break;
739                 }
740             }
741 
FlushBufferedMember(XamlWriter targetWriter)742             void FlushBufferedMember(XamlWriter targetWriter)
743             {
744                 if (this.bufferedMember.IsSet)
745                 {
746                     this.bufferedMember.Flush(targetWriter);
747                     this.bufferedMember = MemberInformation.None;
748                 }
749             }
750 
IsPropertyReferenceExtension(XamlType type)751             bool IsPropertyReferenceExtension(XamlType type)
752             {
753                 return type != null && type.IsGeneric && type.UnderlyingType != null && type.Name == "PropertyReferenceExtension"
754                     && type.UnderlyingType.GetGenericTypeDefinition() == typeof(PropertyReferenceExtension<>);
755             }
756 
IsExpectedPropertyReferenceMember(XamlReader reader)757             bool IsExpectedPropertyReferenceMember(XamlReader reader)
758             {
759                 return reader.NodeType == XamlNodeType.StartMember && IsPropertyReferenceExtension(reader.Member.DeclaringType) && reader.Member.Name == "PropertyName";
760             }
761 
ReadPropertyName(XamlReader reader)762             string ReadPropertyName(XamlReader reader)
763             {
764                 Fx.Assert(reader.Member.Name == "PropertyName", "Exepcted PropertyName member");
765                 string result = null;
766                 while (reader.Read() && reader.NodeType != XamlNodeType.EndMember)
767                 {
768                     // For compat with 4.0, we only need to support PropertyName as Value node
769                     if (reader.NodeType == XamlNodeType.Value)
770                     {
771                         string propertyName = reader.Value as string;
772                         if (propertyName != null)
773                         {
774                             result = propertyName;
775                         }
776                     }
777                 }
778                 if (reader.NodeType == XamlNodeType.EndMember)
779                 {
780                     // Our parent will never see this EndMember node so we need to force its
781                     // depth count to decrement
782                     this.parent.ExitObject();
783                 }
784                 return result;
785             }
786 
WritePropertyReferenceCollection(XamlNodeQueue serializedReferences, XamlWriter targetWriter, IXamlLineInfo lineInfo)787             void WritePropertyReferenceCollection(XamlNodeQueue serializedReferences, XamlWriter targetWriter, IXamlLineInfo lineInfo)
788             {
789                 targetWriter.WriteStartMember(this.activityBuilderPropertyReferencesMember, lineInfo);
790                 targetWriter.WriteGetObject(lineInfo);
791                 targetWriter.WriteStartMember(XamlLanguage.Items, lineInfo);
792                 XamlServices.Transform(serializedReferences.Reader, targetWriter, false);
793                 targetWriter.WriteEndMember(lineInfo);
794                 targetWriter.WriteEndObject(lineInfo);
795                 targetWriter.WriteEndMember(lineInfo);
796             }
797 
WritePropertyReferenceFrameToParent(MemberInformation targetMember, string sourceProperty, Frame parentFrame, IXamlLineInfo lineInfo)798             void WritePropertyReferenceFrameToParent(MemberInformation targetMember, string sourceProperty, Frame parentFrame, IXamlLineInfo lineInfo)
799             {
800                 if (parentFrame.PropertyReferences == null)
801                 {
802                     parentFrame.PropertyReferences = new XamlNodeQueue(this.parent.schemaContext);
803                 }
804                 WriteSerializedPropertyReference(parentFrame.PropertyReferences.Writer, lineInfo, targetMember.Member.Name, sourceProperty);
805 
806                 // we didn't write out the target
807                 // StartMember, so suppress the EndMember
808                 parentFrame.SuppressNextEndMember = true;
809             }
810 
WriteSerializedPropertyReference(XamlWriter targetWriter, IXamlLineInfo lineInfo, string targetName, string sourceName)811             void WriteSerializedPropertyReference(XamlWriter targetWriter, IXamlLineInfo lineInfo, string targetName, string sourceName)
812             {
813                 // Line Info for the entire <ActivityPropertyReference> element
814                 // comes from the end of the <PropertyReference> tag
815                 targetWriter.WriteStartObject(this.activityPropertyReferenceXamlType, lineInfo);
816                 targetWriter.WriteStartMember(this.activityPropertyReferenceTargetProperty, lineInfo);
817                 targetWriter.WriteValue(targetName, lineInfo);
818                 targetWriter.WriteEndMember(lineInfo);
819                 if (sourceName != null)
820                 {
821                     targetWriter.WriteStartMember(this.activityPropertyReferenceSourceProperty, lineInfo);
822                     targetWriter.WriteValue(sourceName, lineInfo);
823                     targetWriter.WriteEndMember(lineInfo);
824                 }
825                 targetWriter.WriteEndObject(lineInfo);
826             }
827 
828             struct MemberInformation
829             {
830                 public static MemberInformation None = new MemberInformation();
831 
832                 public XamlMember Member { get; set; }
833                 public int LineNumber { get; set; }
834                 public int LinePosition { get; set; }
835                 public XamlNodeQueue FollowingNamespaces { get; set; }
836 
MemberInformationSystem.Activities.XamlIntegration.DynamicActivityXamlReader.BuilderStack.MemberInformation837                 public MemberInformation(XamlMember member, IXamlLineInfo lineInfo)
838                     : this()
839                 {
840                     Member = member;
841                     if (lineInfo != null)
842                     {
843                         LineNumber = lineInfo.LineNumber;
844                         LinePosition = lineInfo.LinePosition;
845                     }
846                 }
847 
848                 public bool IsSet
849                 {
850                     get { return this.Member != null; }
851                 }
852 
FlushSystem.Activities.XamlIntegration.DynamicActivityXamlReader.BuilderStack.MemberInformation853                 public void Flush(XamlWriter targetWriter)
854                 {
855                     targetWriter.WriteStartMember(Member, LineNumber, LinePosition);
856                     if (FollowingNamespaces != null)
857                     {
858                         XamlServices.Transform(FollowingNamespaces.Reader, targetWriter, false);
859                     }
860                 }
861             }
862 
863             class Frame
864             {
865                 public XamlNodeQueue PropertyReferences { get; set; }
866                 public bool SuppressNextEndMember { get; set; }
867             }
868         }
869 
870         // This class exists to "zip" together <x:Member> property definitions (to be rewritten as <DynamicActivityProperty> nodes)
871         // with their corresponding default values <MyClass.Foo> (to be rewritten as <DynamicActivityProperty.Value> nodes).
872         // Definitions come all at once, but values could come anywhere in the XAML document, so we save them all almost until the end of
873         // the document and write them all out at once using BufferedPropertyList.CopyTo().
874         class BufferedPropertyList
875         {
876             Dictionary<string, ActivityPropertyHolder> propertyHolders;
877             Dictionary<string, ValueHolder> valueHolders;
878             XamlNodeQueue outerNodes;
879             DynamicActivityXamlReader parent;
880 
881             bool alreadyBufferedDefinitions;
882 
BufferedPropertyList(DynamicActivityXamlReader parent)883             public BufferedPropertyList(DynamicActivityXamlReader parent)
884             {
885                 this.parent = parent;
886                 this.outerNodes = new XamlNodeQueue(parent.SchemaContext);
887             }
888 
889             // Called inside of an x:Members--read up to </x:Members>, buffering definitions
BufferDefinitions(DynamicActivityXamlReader parent)890             public void BufferDefinitions(DynamicActivityXamlReader parent)
891             {
892                 XamlReader subReader = parent.innerReader.ReadSubtree();
893                 IXamlLineInfo readerLineInfo = parent.innerReaderLineInfo;
894 
895                 // 1) swap out the start member with <DynamicActivity.Properties>
896                 subReader.Read();
897                 Fx.Assert(subReader.NodeType == XamlNodeType.StartMember && subReader.Member == XamlLanguage.Members,
898                     "Should be inside of x:Members before calling BufferDefinitions");
899                 this.outerNodes.Writer.WriteStartMember(parent.activityReplacementXamlType.GetMember("Properties"), readerLineInfo);
900 
901                 // x:Members directive has no following GetObject, but Properties does since it's not a directive
902                 this.outerNodes.Writer.WriteGetObject(readerLineInfo);
903                 this.outerNodes.Writer.WriteStartMember(XamlLanguage.Items, readerLineInfo);
904 
905                 // 2) process the subnodes and store them in either ActivityPropertyHolders,
906                 // or exigent nodes in the outer node list
907                 bool continueReading = subReader.Read();
908                 while (continueReading)
909                 {
910                     if (subReader.NodeType == XamlNodeType.StartObject
911                         && subReader.Type == XamlLanguage.Property)
912                     {
913                         // we found an x:Property. Store it in an ActivityPropertyHolder
914                         ActivityPropertyHolder newProperty = new ActivityPropertyHolder(parent, subReader.ReadSubtree());
915                         this.PropertyHolders.Add(newProperty.Name, newProperty);
916 
917                         // and stash away a proxy node to map later
918                         this.outerNodes.Writer.WriteValue(newProperty, readerLineInfo);
919 
920                         // ActivityPropertyHolder consumed the subtree, so we don't need to pump a Read() in this path
921                     }
922                     else
923                     {
924                         // it's not an x:Property. Store it in our extra node list
925                         this.outerNodes.Writer.WriteNode(subReader, readerLineInfo);
926                         continueReading = subReader.Read();
927                     }
928                 }
929 
930                 // close the GetObject added above. Note that we are doing EndObject/EndMember after the last node (EndMember)
931                 // rather than inserting EndMember/EndObject before the last EndMember since all EndMembers are interchangable from a state perspective
932                 this.outerNodes.Writer.WriteEndObject(readerLineInfo);
933                 this.outerNodes.Writer.WriteEndMember(readerLineInfo);
934                 subReader.Close();
935 
936                 this.alreadyBufferedDefinitions = true;
937                 FlushValueHolders();
938             }
939 
FlushValueHolders()940             void FlushValueHolders()
941             {
942                 // We've seen all the property definitions we're going to see. Write out any values already accumulated.
943 
944                 // If we have picked up any values already before definitions, process them immediately
945                 // (and throw as usual if corresponding definition doesn't exist)
946                 if (this.valueHolders != null)
947                 {
948                     foreach (KeyValuePair<string, ValueHolder> propertyNameAndValue in this.valueHolders)
949                     {
950                         ProcessDefaultValue(propertyNameAndValue.Key, propertyNameAndValue.Value.PropertyValue, propertyNameAndValue.Value.ValueReader, propertyNameAndValue.Value.ValueReader as IXamlLineInfo);
951                     }
952                     this.valueHolders = null; // So we don't flush it again at close
953                 }
954             }
955 
956             Dictionary<string, ActivityPropertyHolder> PropertyHolders
957             {
958                 get
959                 {
960                     if (this.propertyHolders == null)
961                     {
962                         this.propertyHolders = new Dictionary<string, ActivityPropertyHolder>();
963                     }
964 
965                     return this.propertyHolders;
966                 }
967             }
968 
BufferDefaultValue(string propertyName, XamlMember propertyValue, XamlReader reader, IXamlLineInfo lineInfo)969             public void BufferDefaultValue(string propertyName, XamlMember propertyValue, XamlReader reader, IXamlLineInfo lineInfo)
970             {
971                 if (this.alreadyBufferedDefinitions)
972                 {
973                     ProcessDefaultValue(propertyName, propertyValue, reader.ReadSubtree(), lineInfo);
974                 }
975                 else
976                 {
977                     if (this.valueHolders == null)
978                     {
979                         this.valueHolders = new Dictionary<string, ValueHolder>();
980                     }
981                     ValueHolder savedValue = new ValueHolder(this.parent.SchemaContext, propertyValue, reader, lineInfo);
982                     valueHolders[propertyName] = savedValue;
983                 }
984             }
985 
ProcessDefaultValue(string propertyName, XamlMember propertyValue, XamlReader reader, IXamlLineInfo lineInfo)986             public void ProcessDefaultValue(string propertyName, XamlMember propertyValue, XamlReader reader, IXamlLineInfo lineInfo)
987             {
988                 ActivityPropertyHolder propertyHolder;
989                 if (!this.PropertyHolders.TryGetValue(propertyName, out propertyHolder))
990                 {
991                     throw FxTrace.Exception.AsError(CreateXamlException(SR.InvalidProperty(propertyName), lineInfo));
992                 }
993 
994                 propertyHolder.ProcessDefaultValue(propertyValue, reader, lineInfo);
995             }
996 
FlushTo(XamlNodeQueue targetNodeQueue, DynamicActivityXamlReader parent)997             public void FlushTo(XamlNodeQueue targetNodeQueue, DynamicActivityXamlReader parent)
998             {
999                 FlushValueHolders();
1000 
1001                 XamlReader sourceReader = this.outerNodes.Reader;
1002                 IXamlLineInfo sourceReaderLineInfo = parent.hasLineInfo ? sourceReader as IXamlLineInfo : null;
1003                 while (sourceReader.Read())
1004                 {
1005                     if (sourceReader.NodeType == XamlNodeType.Value)
1006                     {
1007                         ActivityPropertyHolder propertyHolder = sourceReader.Value as ActivityPropertyHolder;
1008                         if (propertyHolder != null)
1009                         {
1010                             // replace ActivityPropertyHolder with its constituent nodes
1011                             propertyHolder.CopyTo(targetNodeQueue, sourceReaderLineInfo);
1012                             continue;
1013                         }
1014                     }
1015 
1016                     targetNodeQueue.Writer.WriteNode(sourceReader, sourceReaderLineInfo);
1017                 }
1018             }
1019 
1020             // Buffer property values until we can match them with definitions
1021             class ValueHolder
1022             {
1023                 XamlNodeQueue nodes;
1024 
ValueHolder(XamlSchemaContext schemaContext, XamlMember propertyValue, XamlReader reader, IXamlLineInfo lineInfo)1025                 public ValueHolder(XamlSchemaContext schemaContext, XamlMember propertyValue, XamlReader reader, IXamlLineInfo lineInfo)
1026                 {
1027                     this.nodes = new XamlNodeQueue(schemaContext);
1028                     this.PropertyValue = propertyValue;
1029                     XamlWriterExtensions.Transform(reader.ReadSubtree(), this.nodes.Writer, lineInfo, true);
1030                 }
1031 
1032                 public XamlMember PropertyValue { get; private set; }
1033 
1034                 public XamlReader ValueReader
1035                 {
1036                     get
1037                     {
1038                         return this.nodes.Reader;
1039                     }
1040                 }
1041             }
1042 
1043             class ActivityPropertyHolder
1044             {
1045                 // the nodes that we'll pump at the end
1046                 XamlNodeQueue nodes;
1047                 DynamicActivityXamlReader parent;
1048 
ActivityPropertyHolder(DynamicActivityXamlReader parent, XamlReader reader)1049                 public ActivityPropertyHolder(DynamicActivityXamlReader parent, XamlReader reader)
1050                 {
1051                     this.parent = parent;
1052                     this.nodes = new XamlNodeQueue(parent.SchemaContext);
1053                     IXamlLineInfo readerLineInfo = parent.innerReaderLineInfo;
1054 
1055                     // parse the subtree, and extract out the Name and Type for now.
1056                     // keep the node-list open for now, just in case a default value appears
1057                     // later in the document
1058 
1059                     // Rewrite "<x:Property>" to "<DynamicActivityProperty>"
1060                     reader.Read();
1061                     this.nodes.Writer.WriteStartObject(parent.activityPropertyXamlType, readerLineInfo);
1062                     int depth = 1;
1063                     int nameDepth = 0;
1064                     int typeDepth = 0;
1065                     bool continueReading = reader.Read();
1066                     while (continueReading)
1067                     {
1068                         switch (reader.NodeType)
1069                         {
1070                             case XamlNodeType.StartMember:
1071                                 // map <x:Property> membes to the appropriate <DynamicActivity.Property> members
1072                                 if (reader.Member.DeclaringType == XamlLanguage.Property)
1073                                 {
1074                                     XamlMember mappedMember = reader.Member;
1075 
1076                                     if (mappedMember == xPropertyName)
1077                                     {
1078                                         mappedMember = parent.activityPropertyName;
1079                                         if (nameDepth == 0)
1080                                         {
1081                                             nameDepth = 1;
1082                                         }
1083                                     }
1084                                     else if (mappedMember == xPropertyType)
1085                                     {
1086                                         mappedMember = parent.activityPropertyType;
1087                                         if (typeDepth == 0)
1088                                         {
1089                                             typeDepth = 1;
1090                                         }
1091                                     }
1092                                     else if (mappedMember == xPropertyAttributes)
1093                                     {
1094                                         mappedMember = parent.activityPropertyAttributes;
1095                                     }
1096                                     else
1097                                     {
1098                                         throw FxTrace.Exception.AsError(CreateXamlException(SR.PropertyMemberNotSupportedByActivityXamlServices(mappedMember.Name), readerLineInfo));
1099                                     }
1100                                     this.nodes.Writer.WriteStartMember(mappedMember, readerLineInfo);
1101                                     continueReading = reader.Read();
1102                                     continue;
1103                                 }
1104                                 break;
1105 
1106                             case XamlNodeType.Value:
1107                                 if (nameDepth == 1)
1108                                 {
1109                                     // We only support property name as an attribute (nameDepth == 1)
1110                                     this.Name = reader.Value as string;
1111                                 }
1112                                 else if (typeDepth == 1)
1113                                 {
1114                                     // We only support property type as an attribute (typeDepth == 1)
1115                                     XamlTypeName xamlTypeName = XamlTypeName.Parse(reader.Value as string, parent.namespaceTable);
1116                                     XamlType xamlType = parent.SchemaContext.GetXamlType(xamlTypeName);
1117                                     if (xamlType == null)
1118                                     {
1119                                         throw FxTrace.Exception.AsError(CreateXamlException(SR.InvalidPropertyType(reader.Value as string, this.Name), readerLineInfo));
1120                                     }
1121                                     this.Type = xamlType;
1122                                 }
1123                                 break;
1124 
1125                             case XamlNodeType.StartObject:
1126                             case XamlNodeType.GetObject:
1127                                 depth++;
1128                                 IncrementIfPositive(ref nameDepth);
1129                                 IncrementIfPositive(ref typeDepth);
1130                                 if (typeDepth > 0 && reader.Type == parent.xamlTypeXamlType)
1131                                 {
1132                                     this.nodes.Writer.WriteStartObject(parent.typeXamlType, readerLineInfo);
1133                                     continueReading = reader.Read();
1134                                     continue;
1135                                 }
1136                                 break;
1137 
1138                             case XamlNodeType.EndObject:
1139                                 depth--;
1140                                 if (depth == 0)
1141                                 {
1142                                     continueReading = reader.Read();
1143                                     continue; // skip this node, we'll close it by hand in CopyTo()
1144                                 }
1145                                 DecrementIfPositive(ref nameDepth);
1146                                 DecrementIfPositive(ref typeDepth);
1147                                 break;
1148 
1149                             case XamlNodeType.EndMember:
1150                                 DecrementIfPositive(ref nameDepth);
1151                                 DecrementIfPositive(ref typeDepth);
1152                                 break;
1153                         }
1154 
1155                         // if we didn't continue (from a mapped case), just copy over
1156                         this.nodes.Writer.WriteNode(reader, readerLineInfo);
1157                         continueReading = reader.Read();
1158                     }
1159 
1160                     reader.Close();
1161                 }
1162 
1163                 public string Name
1164                 {
1165                     get;
1166                     private set;
1167                 }
1168 
1169                 public XamlType Type
1170                 {
1171                     get;
1172                     private set;
1173                 }
1174 
1175                 // called when we've reached the end of the activity and need
1176                 // to extract out the resulting data into our activity-wide node list
CopyTo(XamlNodeQueue targetNodeQueue, IXamlLineInfo readerInfo)1177                 public void CopyTo(XamlNodeQueue targetNodeQueue, IXamlLineInfo readerInfo)
1178                 {
1179                     // first copy any buffered nodes
1180                     XamlServices.Transform(this.nodes.Reader, targetNodeQueue.Writer, false);
1181 
1182                     // then write the end node for this property
1183                     targetNodeQueue.Writer.WriteEndObject(readerInfo);
1184                 }
1185 
ProcessDefaultValue(XamlMember propertyValue, XamlReader subReader, IXamlLineInfo lineInfo)1186                 public void ProcessDefaultValue(XamlMember propertyValue, XamlReader subReader, IXamlLineInfo lineInfo)
1187                 {
1188                     bool addedStartObject = false;
1189 
1190                     // 1) swap out the start member with <ActivityProperty.Value>
1191                     subReader.Read();
1192                     if (!subReader.Member.IsNameValid)
1193                     {
1194                         throw FxTrace.Exception.AsError(CreateXamlException(SR.InvalidXamlMember(subReader.Member.Name), lineInfo));
1195                     }
1196 
1197                     this.nodes.Writer.WriteStartMember(propertyValue, lineInfo);
1198 
1199                     // temporary hack: read past GetObject/StartMember nodes that are added by
1200                     // the XAML stack. This has been fixed in the WPF branch, but we haven't FI'ed that yet
1201                     XamlReader valueReader;
1202                     subReader.Read();
1203                     if (subReader.NodeType == XamlNodeType.GetObject)
1204                     {
1205                         subReader.Read();
1206                         subReader.Read();
1207                         valueReader = subReader.ReadSubtree();
1208                         valueReader.Read();
1209                     }
1210                     else
1211                     {
1212                         valueReader = subReader;
1213                     }
1214 
1215                     // Add SO tag if necessary UNLESS there's no value to wrap (which means we're already at EO)
1216                     if (valueReader.NodeType != XamlNodeType.EndMember && valueReader.NodeType != XamlNodeType.StartObject)
1217                     {
1218                         addedStartObject = true;
1219                         // Add <TypeOfProperty> nodes so that type converters work correctly
1220                         this.nodes.Writer.WriteStartObject(this.Type, lineInfo);
1221                         this.nodes.Writer.WriteStartMember(XamlLanguage.Initialization, lineInfo);
1222                     }
1223 
1224                     // 3) copy over the value
1225                     while (!valueReader.IsEof)
1226                     {
1227                         this.nodes.Writer.WriteNode(valueReader, lineInfo);
1228                         valueReader.Read();
1229                     }
1230 
1231                     valueReader.Close();
1232 
1233                     // 4) close up the extra nodes
1234                     if (!object.ReferenceEquals(valueReader, subReader))
1235                     {
1236                         subReader.Read();
1237                         while (subReader.Read())
1238                         {
1239                             this.nodes.Writer.WriteNode(subReader, lineInfo);
1240                         }
1241 
1242                     }
1243 
1244                     if (addedStartObject)
1245                     {
1246                         this.nodes.Writer.WriteEndObject(lineInfo);
1247                         this.nodes.Writer.WriteEndMember(lineInfo);
1248                     }
1249                     subReader.Close();
1250                 }
1251             }
1252         }
1253     }
1254 }
1255