1 //----------------------------------------------------------------
2 // Copyright (c) Microsoft Corporation.  All rights reserved.
3 //----------------------------------------------------------------
4 
5 namespace System.Activities.XamlIntegration
6 {
7     using System.Collections.Generic;
8     using System.Globalization;
9     using System.IO;
10     using System.Linq;
11     using System.Runtime;
12     using System.Windows.Markup;
13     using System.Xaml;
14     using System.Xaml.Schema;
15     using System.Xml;
16 
17     // This class rewrites an <ActivityBuilder to <Activity x:Class
18     // ActivityBuilder.Properties is rewritten to x:Members
19     // ActivityBuilder.Name is rewritten as x:Class
20     // ActivityBuilder.Implementation is rewritten as Activity.Implementation
21     //
22     // Because of our [DependsOn] annotations, Name is followed by Attributes, Properties,
23     // Constraints and, lastly, Implementation. The first few relationships are assumed
24     // and enforced through our state machine here to avoid buffering the whole node stream
25     // in common cases (such as no attributes specified).
26     class ActivityBuilderXamlWriter : XamlWriter
27     {
28         readonly XamlWriter innerWriter;
29 
30         // These may be a closed generic type in the Activity<T> case (or null if not an ActivityBuilder),
31         // so we need to compute this value dynamically
32         XamlType activityBuilderXamlType;
33         XamlType activityXamlType;
34 
35         XamlType activityPropertyXamlType;
36         XamlType xamlTypeXamlType;
37         XamlType typeXamlType;
38         XamlType activityPropertyReferenceXamlType;
39 
40         XamlMember activityPropertyType;
41         XamlMember activityPropertyName;
42         XamlMember activityPropertyValue;
43         XamlMember activityBuilderName;
44         XamlMember activityBuilderAttributes;
45         XamlMember activityBuilderProperties;
46         XamlMember activityBuilderPropertyReference;
47         XamlMember activityBuilderPropertyReferences;
48 
49         bool notRewriting;
50         int currentDepth;
51 
52         // we need to accrue namespace so that we can resolve DynamicActivityProperty.Type
53         // and correctly strip superfluous wrapper nodes around default values
54         NamespaceTable namespaceTable;
55         BuilderXamlNode currentState;
56         Stack<BuilderXamlNode> pendingStates;
57 
ActivityBuilderXamlWriter(XamlWriter innerWriter)58         public ActivityBuilderXamlWriter(XamlWriter innerWriter)
59             : base()
60         {
61             this.innerWriter = innerWriter;
62             this.currentState = new RootNode(this);
63             this.namespaceTable = new NamespaceTable();
64         }
65 
66         public override XamlSchemaContext SchemaContext
67         {
68             get
69             {
70                 return this.innerWriter.SchemaContext;
71             }
72         }
73 
SetActivityType(XamlType activityXamlType, XamlType activityBuilderXamlType)74         void SetActivityType(XamlType activityXamlType, XamlType activityBuilderXamlType)
75         {
76             if (activityXamlType == null)
77             {
78                 this.notRewriting = true;
79             }
80             else
81             {
82                 this.activityXamlType = activityXamlType;
83                 this.activityBuilderXamlType = activityBuilderXamlType;
84                 this.xamlTypeXamlType = this.SchemaContext.GetXamlType(typeof(XamlType));
85                 this.typeXamlType = this.SchemaContext.GetXamlType(typeof(Type));
86 
87                 this.activityPropertyXamlType = this.SchemaContext.GetXamlType(typeof(DynamicActivityProperty));
88                 this.activityPropertyType = this.activityPropertyXamlType.GetMember("Type");
89                 this.activityPropertyName = this.activityPropertyXamlType.GetMember("Name");
90                 this.activityPropertyValue = this.activityPropertyXamlType.GetMember("Value");
91 
92                 this.activityBuilderName = this.activityBuilderXamlType.GetMember("Name");
93                 this.activityBuilderAttributes = this.activityBuilderXamlType.GetMember("Attributes");
94                 this.activityBuilderProperties = this.activityBuilderXamlType.GetMember("Properties");
95                 this.activityBuilderPropertyReference = this.SchemaContext.GetXamlType(typeof(ActivityBuilder)).GetAttachableMember("PropertyReference");
96                 this.activityBuilderPropertyReferences = this.SchemaContext.GetXamlType(typeof(ActivityBuilder)).GetAttachableMember("PropertyReferences");
97                 this.activityPropertyReferenceXamlType = this.SchemaContext.GetXamlType(typeof(ActivityPropertyReference));
98             }
99         }
100 
WriteNamespace(NamespaceDeclaration namespaceDeclaration)101         public override void WriteNamespace(NamespaceDeclaration namespaceDeclaration)
102         {
103             if (this.notRewriting)
104             {
105                 this.innerWriter.WriteNamespace(namespaceDeclaration);
106                 return;
107             }
108 
109             if (this.namespaceTable != null)
110             {
111                 this.namespaceTable.AddNamespace(namespaceDeclaration);
112             }
113             this.currentState.WriteNamespace(namespaceDeclaration);
114         }
115 
WriteValue(object value)116         public override void WriteValue(object value)
117         {
118             if (this.notRewriting)
119             {
120                 this.innerWriter.WriteValue(value);
121                 return;
122             }
123 
124             this.currentState.WriteValue(value);
125         }
126 
WriteStartObject(XamlType xamlType)127         public override void WriteStartObject(XamlType xamlType)
128         {
129             if (this.notRewriting)
130             {
131                 this.innerWriter.WriteStartObject(xamlType);
132                 return;
133             }
134 
135             EnterDepth();
136             this.currentState.WriteStartObject(xamlType);
137         }
138 
WriteGetObject()139         public override void WriteGetObject()
140         {
141             if (this.notRewriting)
142             {
143                 this.innerWriter.WriteGetObject();
144                 return;
145             }
146 
147             EnterDepth();
148             this.currentState.WriteGetObject();
149         }
150 
WriteEndObject()151         public override void WriteEndObject()
152         {
153             if (this.notRewriting)
154             {
155                 this.innerWriter.WriteEndObject();
156                 return;
157             }
158 
159             this.currentState.WriteEndObject();
160             ExitDepth();
161         }
162 
WriteStartMember(XamlMember xamlMember)163         public override void WriteStartMember(XamlMember xamlMember)
164         {
165             if (this.notRewriting)
166             {
167                 this.innerWriter.WriteStartMember(xamlMember);
168                 return;
169             }
170 
171             EnterDepth();
172             this.currentState.WriteStartMember(xamlMember);
173         }
174 
WriteEndMember()175         public override void WriteEndMember()
176         {
177             if (this.notRewriting)
178             {
179                 this.innerWriter.WriteEndMember();
180                 return;
181             }
182 
183             this.currentState.WriteEndMember();
184             ExitDepth();
185         }
186 
PushState(BuilderXamlNode state)187         void PushState(BuilderXamlNode state)
188         {
189             if (this.pendingStates == null)
190             {
191                 this.pendingStates = new Stack<BuilderXamlNode>();
192             }
193             this.pendingStates.Push(this.currentState);
194             this.currentState = state;
195         }
196 
EnterDepth()197         void EnterDepth()
198         {
199             Fx.Assert(!this.notRewriting, "we only use depth calculation if we're rewriting");
200             this.currentDepth++;
201             if (this.namespaceTable != null)
202             {
203                 this.namespaceTable.EnterScope();
204             }
205         }
206 
ExitDepth()207         void ExitDepth()
208         {
209             Fx.Assert(!this.notRewriting, "we only use depth calculation if we're rewriting");
210             if (this.currentState.Depth == this.currentDepth)
211             {
212                 // complete the current state
213                 this.currentState.Complete();
214 
215                 // and pop off the next state to look for
216                 if (this.pendingStates.Count > 0)
217                 {
218                     this.currentState = this.pendingStates.Pop();
219                 }
220             }
221             this.currentDepth--;
222             if (this.namespaceTable != null)
223             {
224                 this.namespaceTable.ExitScope();
225             }
226         }
227 
Dispose(bool disposing)228         protected override void Dispose(bool disposing)
229         {
230             base.Dispose(disposing);
231             if (disposing)
232             {
233                 ((IDisposable)this.innerWriter).Dispose();
234             }
235         }
236 
237         abstract class BuilderXamlNode
238         {
BuilderXamlNode(ActivityBuilderXamlWriter writer)239             protected BuilderXamlNode(ActivityBuilderXamlWriter writer)
240             {
241                 this.Depth = writer.currentDepth;
242                 this.Writer = writer;
243                 this.CurrentWriter = writer.innerWriter;
244             }
245 
246             public int Depth
247             {
248                 get;
249                 private set;
250             }
251 
252             // a lot of nodes just redirect output, this
253             // allows them to avoid overriding everything just for that
254             public XamlWriter CurrentWriter
255             {
256                 get;
257                 protected set;
258             }
259 
260             protected ActivityBuilderXamlWriter Writer
261             {
262                 get;
263                 private set;
264             }
265 
Complete()266             protected internal virtual void Complete()
267             {
268             }
269 
WriteNamespace(NamespaceDeclaration namespaceDeclaration)270             protected internal virtual void WriteNamespace(NamespaceDeclaration namespaceDeclaration)
271             {
272                 CurrentWriter.WriteNamespace(namespaceDeclaration);
273             }
274 
WriteStartObject(XamlType xamlType)275             protected internal virtual void WriteStartObject(XamlType xamlType)
276             {
277                 CurrentWriter.WriteStartObject(xamlType);
278             }
279 
WriteGetObject()280             protected internal virtual void WriteGetObject()
281             {
282                 CurrentWriter.WriteGetObject();
283             }
284 
WriteEndObject()285             protected internal virtual void WriteEndObject()
286             {
287                 CurrentWriter.WriteEndObject();
288             }
289 
WriteStartMember(XamlMember xamlMember)290             protected internal virtual void WriteStartMember(XamlMember xamlMember)
291             {
292                 CurrentWriter.WriteStartMember(xamlMember);
293             }
294 
WriteEndMember()295             protected internal virtual void WriteEndMember()
296             {
297                 CurrentWriter.WriteEndMember();
298             }
299 
WriteValue(object value)300             protected internal virtual void WriteValue(object value)
301             {
302                 CurrentWriter.WriteValue(value);
303             }
304         }
305 
306         // RootNode needs to buffer nodes until we finish processing Name + Properties
307         // because we need to insert our namespace _before_ the first StartObject.
308         // this is the starting value for ActivityBuilderXamlWriter.currentNode
309         class RootNode : BuilderXamlNode
310         {
311             const string PreferredXamlNamespaceAlias = "x";
312             const string PreferredClassAlias = "this";
313 
314             bool wroteXamlNamespace;
315             HashSet<string> rootLevelPrefixes;
316 
317             XamlNodeQueue pendingNodes;
318 
RootNode(ActivityBuilderXamlWriter writer)319             public RootNode(ActivityBuilderXamlWriter writer)
320                 : base(writer)
321             {
322                 this.pendingNodes = new XamlNodeQueue(writer.SchemaContext);
323                 base.CurrentWriter = this.pendingNodes.Writer;
324             }
325 
WriteNamespace(NamespaceDeclaration namespaceDeclaration)326             protected internal override void WriteNamespace(NamespaceDeclaration namespaceDeclaration)
327             {
328                 if (Writer.currentDepth == 0 && !this.wroteXamlNamespace)
329                 {
330                     if (namespaceDeclaration.Namespace == XamlLanguage.Xaml2006Namespace)
331                     {
332                         this.wroteXamlNamespace = true;
333                     }
334                     else
335                     {
336                         if (this.rootLevelPrefixes == null)
337                         {
338                             this.rootLevelPrefixes = new HashSet<string>();
339                         }
340                         this.rootLevelPrefixes.Add(namespaceDeclaration.Prefix);
341                     }
342                 }
343                 base.WriteNamespace(namespaceDeclaration);
344             }
345 
WriteStartObject(XamlType xamlType)346             protected internal override void WriteStartObject(XamlType xamlType)
347             {
348                 if (Writer.currentDepth == 1)
349                 {
350                     XamlType activityXamlType = null;
351 
352                     // root object: see if we're serializing an ActivityBuilder
353                     if (xamlType.UnderlyingType == typeof(ActivityBuilder))
354                     {
355                         activityXamlType = Writer.SchemaContext.GetXamlType(typeof(Activity));
356                     }
357                     // or an ActivityBuilder<TResult>
358                     else if (xamlType.IsGeneric && xamlType.UnderlyingType != null
359                         && xamlType.UnderlyingType.GetGenericTypeDefinition() == typeof(ActivityBuilder<>))
360                     {
361                         Type activityType = xamlType.TypeArguments[0].UnderlyingType;
362                         activityXamlType = Writer.SchemaContext.GetXamlType(typeof(Activity<>).MakeGenericType(activityType));
363                     }
364 
365                     Writer.SetActivityType(activityXamlType, xamlType);
366 
367                     if (activityXamlType != null)
368                     {
369                         Writer.PushState(new BuilderClassNode(this, Writer));
370                         return;
371                     }
372                     else
373                     {
374                         // we should be a pass through. Flush any buffered nodes and get out of the way
375                         FlushPendingNodes(null);
376                     }
377                 }
378                 base.WriteStartObject(xamlType);
379             }
380 
FlushPendingNodes(string classNamespace)381             public void FlushPendingNodes(string classNamespace)
382             {
383                 base.CurrentWriter = this.Writer.innerWriter;
384                 if (!Writer.notRewriting)
385                 {
386                     // make sure we have any required namespaces
387                     if (!this.wroteXamlNamespace)
388                     {
389                         string xamlNamespaceAlias = GenerateNamespacePrefix(PreferredXamlNamespaceAlias);
390                         this.WriteNamespace(new NamespaceDeclaration(XamlLanguage.Xaml2006Namespace, xamlNamespaceAlias));
391                     }
392 
393                     // If there's an x:Class="Foo.Bar", add a namespace declaration for Foo in the local assembly so we can
394                     // say stuff like this:Bar.MyProperty later on. DON'T add the namespace declaration if somebody has already
395                     // declared the namespace in the nodestream though (duplicates are an error).
396                     if (classNamespace != null)
397                     {
398                         bool sawClassNamespace = false;
399 
400                         XamlReader reader = this.pendingNodes.Reader;
401                         XamlWriter writer = this.Writer.innerWriter;
402                         while (reader.Read() && reader.NodeType == XamlNodeType.NamespaceDeclaration)
403                         {
404                             if (classNamespace.Equals(reader.Namespace.Namespace))
405                             {
406                                 sawClassNamespace = true;
407                             }
408                             writer.WriteNode(reader);
409                         }
410 
411                         if (!sawClassNamespace)
412                         {
413                             string classNamespaceAlias = GenerateNamespacePrefix(PreferredClassAlias);
414                             writer.WriteNamespace(new NamespaceDeclaration(classNamespace, classNamespaceAlias));
415                         }
416 
417                         // We may have consumed the first non-namespace node off the reader in order
418                         // to check it for being a NamespaceDeclaration. Make sure it still gets written.
419                         if (!reader.IsEof)
420                         {
421                             writer.WriteNode(reader);
422                         }
423                     }
424 
425                     this.rootLevelPrefixes = null; // not needed anymore
426                 }
427 
428                 XamlServices.Transform(this.pendingNodes.Reader, this.Writer.innerWriter, false);
429                 this.pendingNodes = null;
430             }
431 
GenerateNamespacePrefix(string desiredPrefix)432             string GenerateNamespacePrefix(string desiredPrefix)
433             {
434                 string aliasPostfix = string.Empty;
435                 // try postfixing 1-1000 first
436                 for (int i = 1; i <= 1000; i++)
437                 {
438                     string alias = desiredPrefix + aliasPostfix;
439                     if (!this.rootLevelPrefixes.Contains(alias))
440                     {
441                         return alias;
442                     }
443                     aliasPostfix = i.ToString(CultureInfo.InvariantCulture);
444                 }
445 
446                 // fall back to GUID
447                 return desiredPrefix + Guid.NewGuid().ToString();
448             }
449         }
450 
451         // <ActivityBuilder>...</ActivityBuilder>
452         class BuilderClassNode : BuilderXamlNode
453         {
454             RootNode rootNode;
455 
456             string xClassNamespace;
457             XamlType xClassXamlType;
458             XamlNodeQueue xClassNodes;
459             XamlNodeQueue xClassAttributeNodes;
460             XamlNodeQueue xPropertiesNodes;
461             XamlNodeQueue otherNodes;
462             List<KeyValuePair<string, XamlNodeQueue>> defaultValueNodes;
463 
BuilderClassNode(RootNode rootNode, ActivityBuilderXamlWriter writer)464             public BuilderClassNode(RootNode rootNode, ActivityBuilderXamlWriter writer)
465                 : base(writer)
466             {
467                 this.rootNode = rootNode;
468 
469                 // by default, if we're not in a special sub-tree, ferret the nodes away on the side
470                 this.otherNodes = new XamlNodeQueue(writer.SchemaContext);
471                 base.CurrentWriter = this.otherNodes.Writer;
472             }
473 
SetXClass(string builderName, XamlNodeQueue nameNodes)474             public void SetXClass(string builderName, XamlNodeQueue nameNodes)
475             {
476                 this.xClassNodes = new XamlNodeQueue(Writer.SchemaContext);
477                 this.xClassNodes.Writer.WriteStartMember(XamlLanguage.Class);
478                 this.xClassNamespace = null;
479                 string xClassName = builderName;
480                 if (string.IsNullOrEmpty(xClassName))
481                 {
482                     xClassName = string.Format(CultureInfo.CurrentCulture, "_{0}", Guid.NewGuid().ToString().Replace("-", string.Empty).Substring(0, 4));
483                 }
484 
485                 if (nameNodes != null)
486                 {
487                     XamlServices.Transform(nameNodes.Reader, this.xClassNodes.Writer, false);
488                 }
489                 else
490                 {
491                     this.xClassNodes.Writer.WriteValue(xClassName);
492                     this.xClassNodes.Writer.WriteEndMember();
493                 }
494 
495                 int nameStartIndex = xClassName.LastIndexOf('.');
496                 if (nameStartIndex > 0)
497                 {
498                     this.xClassNamespace = builderName.Substring(0, nameStartIndex);
499                     xClassName = builderName.Substring(nameStartIndex + 1);
500                 }
501 
502                 this.xClassNamespace = string.Format(CultureInfo.CurrentUICulture, "clr-namespace:{0}", this.xClassNamespace ?? string.Empty);
503                 this.xClassXamlType = new XamlType(this.xClassNamespace, xClassName, null, Writer.SchemaContext);
504             }
505 
506             // Attributes [DependsOn("Name")]
SetAttributes(XamlNodeQueue attributeNodes)507             public void SetAttributes(XamlNodeQueue attributeNodes)
508             {
509                 this.xClassAttributeNodes = attributeNodes;
510             }
511 
512             // Properties [DependsOn("Attributes")]
SetProperties(XamlNodeQueue propertyNodes, List<KeyValuePair<string, XamlNodeQueue>> defaultValueNodes)513             public void SetProperties(XamlNodeQueue propertyNodes, List<KeyValuePair<string, XamlNodeQueue>> defaultValueNodes)
514             {
515                 this.xPropertiesNodes = propertyNodes;
516                 this.defaultValueNodes = defaultValueNodes;
517 
518                 // exiting the properties tag. So we've now accrued any instances of Name and Attributes
519                 // that could possibly be hit flush our preamble
520                 FlushPreamble();
521             }
522 
FlushPreamble()523             void FlushPreamble()
524             {
525                 if (this.otherNodes == null) // already flushed
526                 {
527                     return;
528                 }
529                 CurrentWriter = this.Writer.innerWriter;
530                 string classNamespace = null;
531                 // first, see if we need to emit a namespace corresponding to our class
532                 if (this.defaultValueNodes != null)
533                 {
534                     classNamespace = this.xClassNamespace;
535                 }
536 
537                 this.rootNode.FlushPendingNodes(classNamespace);
538                 this.rootNode = null; // not needed anymore
539 
540                 CurrentWriter.WriteStartObject(this.Writer.activityXamlType);
541 
542                 // first dump x:Class
543                 if (this.xClassNodes == null)
544                 {
545                     SetXClass(null, null); // this will setup a default
546                 }
547                 XamlServices.Transform(this.xClassNodes.Reader, CurrentWriter, false);
548 
549                 // String default values get written in attribute form immediately.
550                 // Other values get deferred until after x:Members, etc.
551                 XamlNodeQueue deferredPropertyNodes = null;
552                 if (this.defaultValueNodes != null)
553                 {
554                     foreach (KeyValuePair<string, XamlNodeQueue> defaultValueNode in this.defaultValueNodes)
555                     {
556                         XamlReader reader = defaultValueNode.Value.Reader;
557                         if (reader.Read())
558                         {
559                             bool isStringValue = false;
560                             if (reader.NodeType == XamlNodeType.Value)
561                             {
562                                 string stringValue = reader.Value as string;
563                                 if (stringValue != null)
564                                 {
565                                     isStringValue = true;
566                                 }
567                             }
568                             if (isStringValue)
569                             {
570                                 CurrentWriter.WriteStartMember(new XamlMember(defaultValueNode.Key, this.xClassXamlType, true));
571                                 CurrentWriter.WriteNode(reader);
572                                 XamlServices.Transform(defaultValueNode.Value.Reader, CurrentWriter, false);
573                                 // don't need an EndMember since it will be sitting in the node list (we only needed to strip the StartMember)
574                             }
575                             else
576                             {
577                                 // Else: We'll write this out in a minute, after the x:ClassAttributes and x:Properties
578                                 if (deferredPropertyNodes == null)
579                                 {
580                                     deferredPropertyNodes = new XamlNodeQueue(Writer.SchemaContext);
581                                 }
582                                 deferredPropertyNodes.Writer.WriteStartMember(new XamlMember(defaultValueNode.Key, this.xClassXamlType, true));
583                                 deferredPropertyNodes.Writer.WriteNode(reader);
584                                 XamlServices.Transform(defaultValueNode.Value.Reader, deferredPropertyNodes.Writer, false);
585                             }
586                         }
587                     }
588                 }
589 
590                 // then dump x:ClassAttributes if we have any
591                 if (this.xClassAttributeNodes != null)
592                 {
593                     XamlServices.Transform(this.xClassAttributeNodes.Reader, CurrentWriter, false);
594                 }
595 
596                 // and x:Properties
597                 if (this.xPropertiesNodes != null)
598                 {
599                     XamlServices.Transform(this.xPropertiesNodes.Reader, CurrentWriter, false);
600                 }
601 
602                 if (deferredPropertyNodes != null)
603                 {
604                     XamlServices.Transform(deferredPropertyNodes.Reader, CurrentWriter, false);
605                 }
606 
607                 if (this.otherNodes.Count > 0)
608                 {
609                     XamlServices.Transform(this.otherNodes.Reader, CurrentWriter, false);
610                 }
611                 this.otherNodes = null; // done with this
612             }
613 
Complete()614             protected internal override void Complete()
615             {
616                 if (this.otherNodes != null)
617                 {
618                     // need to flush
619                     FlushPreamble();
620                 }
621             }
622 
WriteStartMember(XamlMember xamlMember)623             protected internal override void WriteStartMember(XamlMember xamlMember)
624             {
625                 if (Writer.currentDepth == this.Depth + 1 && !xamlMember.IsAttachable)
626                 {
627                     if (xamlMember == Writer.activityBuilderName)
628                     {
629                         // record that we're in ActivityBuilder.Name, since we'll need the class name for
630                         // default value output
631                         Writer.PushState(new BuilderNameNode(this, Writer));
632                         return;
633                     }
634                     else if (xamlMember == Writer.activityBuilderAttributes)
635                     {
636                         // rewrite ActivityBuilder.Attributes to x:ClassAttributes
637                         Writer.PushState(new AttributesNode(this, Writer));
638                         return;
639                     }
640                     else if (xamlMember == Writer.activityBuilderProperties)
641                     {
642                         // rewrite ActivityBuilder.Properties to x:Members
643                         Writer.PushState(new PropertiesNode(this, Writer));
644                         return;
645                     }
646                     else
647                     {
648                         // any other member means we've passed properties due to [DependsOn] relationships
649                         FlushPreamble();
650                         if (xamlMember.DeclaringType == Writer.activityBuilderXamlType)
651                         {
652                             // Rewrite "<ActivityBuilder.XXX>" to "<Activity.XXX>"
653                             xamlMember = Writer.activityXamlType.GetMember(xamlMember.Name);
654                             if (xamlMember == null)
655                             {
656                                 throw FxTrace.Exception.AsError(new InvalidOperationException(
657                                     SR.MemberNotSupportedByActivityXamlServices(xamlMember.Name)));
658                             }
659 
660                             if (xamlMember.Name == "Implementation")
661                             {
662                                 Writer.PushState(new ImplementationNode(Writer));
663                             }
664                         }
665                     }
666                 }
667                 base.WriteStartMember(xamlMember);
668             }
669         }
670 
671         // <ActivityBuilder.Name> node that we'll map to x:Class
672         class BuilderNameNode : BuilderXamlNode
673         {
674             BuilderClassNode classNode;
675             string builderName;
676             XamlNodeQueue nameNodes;
677 
BuilderNameNode(BuilderClassNode classNode, ActivityBuilderXamlWriter writer)678             public BuilderNameNode(BuilderClassNode classNode, ActivityBuilderXamlWriter writer)
679                 : base(writer)
680             {
681                 this.classNode = classNode;
682                 this.nameNodes = new XamlNodeQueue(writer.SchemaContext);
683                 base.CurrentWriter = this.nameNodes.Writer;
684             }
685 
Complete()686             protected internal override void Complete()
687             {
688                 this.classNode.SetXClass(this.builderName, this.nameNodes);
689             }
690 
WriteValue(object value)691             protected internal override void WriteValue(object value)
692             {
693                 if (Writer.currentDepth == this.Depth)
694                 {
695                     this.builderName = (string)value;
696                 }
697 
698                 base.WriteValue(value);
699             }
700         }
701 
702         // <ActivityBuilder.Attributes> node that we'll map to x:ClassAttributes
703         class AttributesNode : BuilderXamlNode
704         {
705             XamlNodeQueue attributeNodes;
706             BuilderClassNode classNode;
707 
AttributesNode(BuilderClassNode classNode, ActivityBuilderXamlWriter writer)708             public AttributesNode(BuilderClassNode classNode, ActivityBuilderXamlWriter writer)
709                 : base(writer)
710             {
711                 this.classNode = classNode;
712                 this.attributeNodes = new XamlNodeQueue(writer.SchemaContext);
713                 base.CurrentWriter = this.attributeNodes.Writer;
714                 CurrentWriter.WriteStartMember(XamlLanguage.ClassAttributes);
715             }
716 
Complete()717             protected internal override void Complete()
718             {
719                 this.classNode.SetAttributes(this.attributeNodes);
720             }
721         }
722 
723         // <ActivityBuilder.Properties> node that we'll map to x:Members
724         // since x:Members doesn't have GetObject/StartMember wrappers around the value, we need to eat those
725         class PropertiesNode : BuilderXamlNode
726         {
727             List<KeyValuePair<string, XamlNodeQueue>> defaultValueNodes;
728             XamlNodeQueue propertiesNodes;
729             BuilderClassNode classNode;
730             bool skipGetObject;
731 
PropertiesNode(BuilderClassNode classNode, ActivityBuilderXamlWriter writer)732             public PropertiesNode(BuilderClassNode classNode, ActivityBuilderXamlWriter writer)
733                 : base(writer)
734             {
735                 this.classNode = classNode;
736                 this.propertiesNodes = new XamlNodeQueue(writer.SchemaContext);
737                 base.CurrentWriter = this.propertiesNodes.Writer;
738                 CurrentWriter.WriteStartMember(XamlLanguage.Members);
739             }
740 
WriteStartObject(XamlType xamlType)741             protected internal override void WriteStartObject(XamlType xamlType)
742             {
743                 if (xamlType == Writer.activityPropertyXamlType && Writer.currentDepth == this.Depth + 3)
744                 {
745                     xamlType = XamlLanguage.Property;
746                     Writer.PushState(new PropertyNode(this, Writer));
747                 }
748                 base.WriteStartObject(xamlType);
749             }
750 
WriteGetObject()751             protected internal override void WriteGetObject()
752             {
753                 if (Writer.currentDepth == this.Depth + 1)
754                 {
755                     this.skipGetObject = true;
756                 }
757                 else
758                 {
759                     base.WriteGetObject();
760                 }
761             }
762 
WriteEndObject()763             protected internal override void WriteEndObject()
764             {
765                 if (this.skipGetObject && Writer.currentDepth == this.Depth + 1)
766                 {
767                     this.skipGetObject = false;
768                 }
769                 else
770                 {
771                     base.WriteEndObject();
772                 }
773             }
774 
WriteStartMember(XamlMember xamlMember)775             protected internal override void WriteStartMember(XamlMember xamlMember)
776             {
777                 if (this.skipGetObject && Writer.currentDepth == this.Depth + 2)
778                 {
779                     return;
780                 }
781                 base.WriteStartMember(xamlMember);
782             }
783 
WriteEndMember()784             protected internal override void WriteEndMember()
785             {
786                 if (this.skipGetObject && Writer.currentDepth == this.Depth + 2)
787                 {
788                     return;
789                 }
790                 base.WriteEndMember();
791             }
792 
Complete()793             protected internal override void Complete()
794             {
795                 this.classNode.SetProperties(this.propertiesNodes, this.defaultValueNodes);
796             }
797 
AddDefaultValue(string propertyName, XamlNodeQueue value)798             public void AddDefaultValue(string propertyName, XamlNodeQueue value)
799             {
800                 if (this.defaultValueNodes == null)
801                 {
802                     this.defaultValueNodes = new List<KeyValuePair<string, XamlNodeQueue>>();
803                 }
804 
805                 if (string.IsNullOrEmpty(propertyName))
806                 {
807                     // default a name if one doesn't exist
808                     propertyName = string.Format(CultureInfo.CurrentCulture, "_{0}", Guid.NewGuid().ToString().Replace("-", string.Empty));
809                 }
810 
811                 this.defaultValueNodes.Add(new KeyValuePair<string, XamlNodeQueue>(propertyName, value));
812             }
813         }
814 
815         // <DynamicActivityProperty>...</DynamicActivityProperty>
816         class PropertyNode : BuilderXamlNode
817         {
818             PropertiesNode properties;
819             string propertyName;
820             XamlType propertyType;
821             XamlNodeQueue defaultValue;
822 
PropertyNode(PropertiesNode properties, ActivityBuilderXamlWriter writer)823             public PropertyNode(PropertiesNode properties, ActivityBuilderXamlWriter writer)
824                 : base(writer)
825             {
826                 this.properties = properties;
827                 base.CurrentWriter = properties.CurrentWriter;
828             }
829 
SetName(string name)830             public void SetName(string name)
831             {
832                 this.propertyName = name;
833             }
834 
SetType(XamlType type)835             public void SetType(XamlType type)
836             {
837                 this.propertyType = type;
838             }
839 
SetDefaultValue(XamlNodeQueue defaultValue)840             public void SetDefaultValue(XamlNodeQueue defaultValue)
841             {
842                 this.defaultValue = defaultValue;
843             }
844 
WriteStartMember(XamlMember xamlMember)845             protected internal override void WriteStartMember(XamlMember xamlMember)
846             {
847                 if (xamlMember.DeclaringType == Writer.activityPropertyXamlType && Writer.currentDepth == this.Depth + 1)
848                 {
849                     if (xamlMember == Writer.activityPropertyName)
850                     {
851                         // record that we're in a property name, since we'll need this for default value output
852                         Writer.PushState(new PropertyNameNode(this, Writer));
853                         xamlMember = DynamicActivityXamlReader.xPropertyName;
854                     }
855                     else if (xamlMember == Writer.activityPropertyType)
856                     {
857                         // record that we're in a property type, since we'll need this for default value output
858                         Writer.PushState(new PropertyTypeNode(this, Writer));
859                         xamlMember = DynamicActivityXamlReader.xPropertyType;
860                     }
861                     else if (xamlMember == Writer.activityPropertyValue)
862                     {
863                         // record that we're in a property value, since we'll need this for default value output.
864                         // don't write anything since we'll dump the default values after we exit ActivityBuilder.Properties
865                         Writer.PushState(new PropertyValueNode(this, Writer));
866                         xamlMember = null;
867                     }
868                 }
869 
870                 if (xamlMember != null)
871                 {
872                     base.WriteStartMember(xamlMember);
873                 }
874             }
875 
Complete()876             protected internal override void Complete()
877             {
878                 if (this.defaultValue != null)
879                 {
880                     if (string.IsNullOrEmpty(this.propertyName))
881                     {
882                         // default a name if one doesn't exist
883                         this.propertyName = string.Format(CultureInfo.CurrentCulture, "_{0}", Guid.NewGuid().ToString().Replace("-", string.Empty));
884                     }
885 
886                     if (this.defaultValue != null && this.propertyType != null)
887                     {
888                         // post-process the default value nodes to strip out
889                         // StartObject+StartMember _Initialization+EndMember+EndObject
890                         // wrapper nodes if the type of the object matches the
891                         // property Type (since we are moving from "object Value" to "T Value"
892                         this.defaultValue = StripTypeWrapping(this.defaultValue, this.propertyType);
893                     }
894 
895                     this.properties.AddDefaultValue(this.propertyName, this.defaultValue);
896                 }
897             }
898 
StripTypeWrapping(XamlNodeQueue valueNodes, XamlType propertyType)899             static XamlNodeQueue StripTypeWrapping(XamlNodeQueue valueNodes, XamlType propertyType)
900             {
901                 XamlNodeQueue targetNodes = new XamlNodeQueue(valueNodes.Reader.SchemaContext);
902                 XamlReader source = valueNodes.Reader;
903                 XamlWriter target = targetNodes.Writer;
904                 int depth = 0;
905                 bool consumeWrapperEndTags = false;
906                 bool hasBufferedStartObject = false;
907 
908                 while (source.Read())
909                 {
910                     switch (source.NodeType)
911                     {
912                         case XamlNodeType.StartObject:
913                             depth++;
914                             // only strip the wrapping type nodes if we have exactly this sequence:
915                             // StartObject StartMember(Intialization) Value EndMember EndObject.
916                             if (targetNodes.Count == 0 && depth == 1 && source.Type == propertyType && valueNodes.Count == 5)
917                             {
918                                 hasBufferedStartObject = true;
919                                 continue;
920                             }
921                             break;
922 
923                         case XamlNodeType.GetObject:
924                             depth++;
925                             break;
926 
927                         case XamlNodeType.StartMember:
928                             depth++;
929                             if (hasBufferedStartObject)
930                             {
931                                 if (depth == 2 && source.Member == XamlLanguage.Initialization)
932                                 {
933                                     consumeWrapperEndTags = true;
934                                     continue;
935                                 }
936                                 else
937                                 {
938                                     hasBufferedStartObject = false;
939                                     targetNodes.Writer.WriteStartObject(propertyType);
940                                 }
941                             }
942                             break;
943 
944                         case XamlNodeType.EndMember:
945                             depth--;
946                             if (consumeWrapperEndTags && depth == 1)
947                             {
948                                 continue;
949                             }
950                             break;
951 
952                         case XamlNodeType.EndObject:
953                             depth--;
954                             if (consumeWrapperEndTags && depth == 0)
955                             {
956                                 consumeWrapperEndTags = false;
957                                 continue;
958                             }
959                             break;
960                     }
961 
962                     target.WriteNode(source);
963                 }
964 
965                 return targetNodes;
966             }
967         }
968 
969         // <DynamicActivityProperty.Name>...</DynamicActivityProperty.Name>
970         class PropertyNameNode : BuilderXamlNode
971         {
972             PropertyNode property;
973 
PropertyNameNode(PropertyNode property, ActivityBuilderXamlWriter writer)974             public PropertyNameNode(PropertyNode property, ActivityBuilderXamlWriter writer)
975                 : base(writer)
976             {
977                 this.property = property;
978                 base.CurrentWriter = property.CurrentWriter;
979             }
980 
WriteValue(object value)981             protected internal override void WriteValue(object value)
982             {
983                 if (Writer.currentDepth == this.Depth)
984                 {
985                     property.SetName((string)value);
986                 }
987 
988                 base.WriteValue(value);
989             }
990         }
991 
992         // <DynamicActivityProperty.Type>...</DynamicActivityProperty.Type>
993         class PropertyTypeNode : BuilderXamlNode
994         {
995             PropertyNode property;
996 
PropertyTypeNode(PropertyNode property, ActivityBuilderXamlWriter writer)997             public PropertyTypeNode(PropertyNode property, ActivityBuilderXamlWriter writer)
998                 : base(writer)
999             {
1000                 this.property = property;
1001                 base.CurrentWriter = property.CurrentWriter;
1002             }
1003 
WriteValue(object value)1004             protected internal override void WriteValue(object value)
1005             {
1006                 if (Writer.currentDepth == this.Depth)
1007                 {
1008                     // We only support property type as an attribute
1009                     XamlTypeName xamlTypeName = XamlTypeName.Parse(value as string, Writer.namespaceTable);
1010                     XamlType xamlType = Writer.SchemaContext.GetXamlType(xamlTypeName);
1011                     property.SetType(xamlType); // supports null
1012                 }
1013 
1014                 base.WriteValue(value);
1015             }
1016         }
1017 
1018         // <DynamicActivityProperty.Value>...</DynamicActivityProperty.Value>
1019         class PropertyValueNode : BuilderXamlNode
1020         {
1021             PropertyNode property;
1022             XamlNodeQueue valueNodes;
1023 
PropertyValueNode(PropertyNode property, ActivityBuilderXamlWriter writer)1024             public PropertyValueNode(PropertyNode property, ActivityBuilderXamlWriter writer)
1025                 : base(writer)
1026             {
1027                 this.property = property;
1028                 this.valueNodes = new XamlNodeQueue(writer.SchemaContext);
1029                 base.CurrentWriter = this.valueNodes.Writer;
1030             }
1031 
Complete()1032             protected internal override void Complete()
1033             {
1034                 this.property.SetDefaultValue(this.valueNodes);
1035                 base.Complete();
1036             }
1037         }
1038 
1039         // <ActivityBuilder.Implementation>...</ActivityBuilder.Implementation>
1040         // We need to convert any <ActivityBuilder.PropertyReferences> inside here into <PropertyReferenceExtension>.
1041         class ImplementationNode : BuilderXamlNode
1042         {
1043             Stack<ObjectFrame> objectStack;
1044 
ImplementationNode(ActivityBuilderXamlWriter writer)1045             public ImplementationNode(ActivityBuilderXamlWriter writer)
1046                 : base(writer)
1047             {
1048                 this.objectStack = new Stack<ObjectFrame>();
1049             }
1050 
AddPropertyReference(ActivityPropertyReference propertyReference)1051             internal void AddPropertyReference(ActivityPropertyReference propertyReference)
1052             {
1053                 ObjectFrame currentFrame = this.objectStack.Peek();
1054                 Fx.Assert(currentFrame.Type != null, "Should only create PropertyReferencesNode inside a StartObject");
1055                 if (currentFrame.PropertyReferences == null)
1056                 {
1057                     currentFrame.PropertyReferences = new List<ActivityPropertyReference>();
1058                 }
1059                 currentFrame.PropertyReferences.Add(propertyReference);
1060             }
1061 
SetUntransformedPropertyReferences(XamlMember propertyReferencesMember, XamlNodeQueue untransformedNodes)1062             internal void SetUntransformedPropertyReferences(XamlMember propertyReferencesMember, XamlNodeQueue untransformedNodes)
1063             {
1064                 ObjectFrame currentFrame = this.objectStack.Peek();
1065                 Fx.Assert(currentFrame.Type != null, "Should only create PropertyReferencesNode inside a StartObject");
1066                 currentFrame.AddMember(propertyReferencesMember, untransformedNodes);
1067             }
1068 
WriteStartMember(XamlMember xamlMember)1069             protected internal override void WriteStartMember(XamlMember xamlMember)
1070             {
1071                 ObjectFrame currentFrame = this.objectStack.Peek();
1072                 if (currentFrame.Type == null)
1073                 {
1074                     base.WriteStartMember(xamlMember);
1075                 }
1076                 else if (xamlMember == Writer.activityBuilderPropertyReference || xamlMember == Writer.activityBuilderPropertyReferences)
1077                 {
1078                     // Parse out the contents of <ActivityBuilder.PropertyReferences> using a PropertyReferencesNode
1079                     Writer.PushState(new PropertyReferencesNode(Writer, xamlMember, this));
1080                 }
1081                 else
1082                 {
1083                     this.CurrentWriter = currentFrame.StartMember(xamlMember, CurrentWriter);
1084                 }
1085             }
1086 
WriteStartObject(XamlType xamlType)1087             protected internal override void WriteStartObject(XamlType xamlType)
1088             {
1089                 this.objectStack.Push(new ObjectFrame { Type = xamlType });
1090                 base.WriteStartObject(xamlType);
1091             }
1092 
WriteGetObject()1093             protected internal override void WriteGetObject()
1094             {
1095                 this.objectStack.Push(new ObjectFrame());
1096                 base.WriteGetObject();
1097             }
1098 
WriteEndObject()1099             protected internal override void WriteEndObject()
1100             {
1101                 ObjectFrame frame = this.objectStack.Pop();
1102                 frame.FlushMembers(CurrentWriter);
1103                 base.WriteEndObject();
1104             }
1105 
WriteEndMember()1106             protected internal override void WriteEndMember()
1107             {
1108                 // Stack can be empty here if this is the EndMember that closes out the Node
1109                 ObjectFrame currentFrame = this.objectStack.Count > 0 ? this.objectStack.Peek() : null;
1110                 if (currentFrame == null || currentFrame.Type == null)
1111                 {
1112                     base.WriteEndMember();
1113                 }
1114                 else
1115                 {
1116                     CurrentWriter = currentFrame.EndMember();
1117                 }
1118             }
1119 
1120             class ObjectFrame
1121             {
1122                 XamlWriter parentWriter;
1123                 XamlNodeQueue currentMemberNodes;
1124 
1125                 public XamlType Type { get; set; }
1126                 public XamlMember CurrentMember { get; set; }
1127                 public List<KeyValuePair<XamlMember, XamlNodeQueue>> Members { get; set; }
1128                 public List<ActivityPropertyReference> PropertyReferences { get; set; }
1129 
StartMember(XamlMember member, XamlWriter parentWriter)1130                 public XamlWriter StartMember(XamlMember member, XamlWriter parentWriter)
1131                 {
1132                     this.CurrentMember = member;
1133                     this.parentWriter = parentWriter;
1134                     this.currentMemberNodes = new XamlNodeQueue(parentWriter.SchemaContext);
1135                     return this.currentMemberNodes.Writer;
1136                 }
1137 
EndMember()1138                 public XamlWriter EndMember()
1139                 {
1140                     AddMember(this.CurrentMember, this.currentMemberNodes);
1141                     this.CurrentMember = null;
1142                     this.currentMemberNodes = null;
1143                     XamlWriter parentWriter = this.parentWriter;
1144                     this.parentWriter = null;
1145                     return parentWriter;
1146                 }
1147 
AddMember(XamlMember member, XamlNodeQueue content)1148                 public void AddMember(XamlMember member, XamlNodeQueue content)
1149                 {
1150                     if (this.Members == null)
1151                     {
1152                         this.Members = new List<KeyValuePair<XamlMember, XamlNodeQueue>>();
1153                     }
1154                     this.Members.Add(new KeyValuePair<XamlMember, XamlNodeQueue>(member, content));
1155                 }
1156 
FlushMembers(XamlWriter parentWriter)1157                 public void FlushMembers(XamlWriter parentWriter)
1158                 {
1159                     if (this.Type == null)
1160                     {
1161                         Fx.Assert(Members == null, "We shouldn't buffer members on GetObject");
1162                         return;
1163                     }
1164                     if (Members != null)
1165                     {
1166                         foreach (KeyValuePair<XamlMember, XamlNodeQueue> member in Members)
1167                         {
1168                             parentWriter.WriteStartMember(member.Key);
1169                             XamlServices.Transform(member.Value.Reader, parentWriter, false);
1170                             parentWriter.WriteEndMember();
1171                         }
1172                     }
1173                     if (PropertyReferences != null)
1174                     {
1175                         foreach (ActivityPropertyReference propertyReference in PropertyReferences)
1176                         {
1177                             XamlMember targetProperty = this.Type.GetMember(propertyReference.TargetProperty) ??
1178                                 new XamlMember(propertyReference.TargetProperty, this.Type, false);
1179                             parentWriter.WriteStartMember(targetProperty);
1180                             WritePropertyReference(parentWriter, targetProperty, propertyReference.SourceProperty);
1181                             parentWriter.WriteEndMember();
1182                         }
1183                     }
1184                 }
1185 
WritePropertyReference(XamlWriter parentWriter, XamlMember targetProperty, string sourceProperty)1186                 void WritePropertyReference(XamlWriter parentWriter, XamlMember targetProperty, string sourceProperty)
1187                 {
1188                     Type propertyReferenceType = typeof(PropertyReferenceExtension<>).MakeGenericType(targetProperty.Type.UnderlyingType ?? typeof(object));
1189                     XamlType propertyReferenceXamlType = parentWriter.SchemaContext.GetXamlType(propertyReferenceType);
1190                     parentWriter.WriteStartObject(propertyReferenceXamlType);
1191 
1192                     if (sourceProperty != null)
1193                     {
1194                         parentWriter.WriteStartMember(propertyReferenceXamlType.GetMember("PropertyName"));
1195                         parentWriter.WriteValue(sourceProperty);
1196                         parentWriter.WriteEndMember();
1197                     }
1198 
1199                     parentWriter.WriteEndObject();
1200                 }
1201             }
1202         }
1203 
1204         // <ActivityBuilder.PropertyReference(s)> is stripped out and the inner
1205         // <ActivityPropertyReference>s map to PropertyReferenceNodes
1206         class PropertyReferencesNode : BuilderXamlNode
1207         {
1208             XamlNodeQueue untransformedNodes; // nodes that couldn't be transformed to PropertyReference form
1209             XamlMember originalStartMember;
1210 
PropertyReferencesNode(ActivityBuilderXamlWriter writer, XamlMember originalStartMember, ImplementationNode parent)1211             public PropertyReferencesNode(ActivityBuilderXamlWriter writer, XamlMember originalStartMember, ImplementationNode parent)
1212                 : base(writer)
1213             {
1214                 this.untransformedNodes = new XamlNodeQueue(Writer.SchemaContext);
1215                 this.originalStartMember = originalStartMember;
1216                 this.Parent = parent;
1217                 base.CurrentWriter = this.untransformedNodes.Writer;
1218             }
1219 
1220             public bool HasUntransformedChildren { get; set; }
1221 
1222             public ImplementationNode Parent { get; private set; }
1223 
1224             public XamlWriter UntransformedNodesWriter { get { return this.untransformedNodes.Writer; } }
1225 
WriteStartObject(XamlType xamlType)1226             protected internal override void WriteStartObject(XamlType xamlType)
1227             {
1228                 if (xamlType == Writer.activityPropertyReferenceXamlType)
1229                 {
1230                     Writer.PushState(new PropertyReferenceNode(this.Writer, this));
1231                     return;
1232                 }
1233                 base.WriteStartObject(xamlType);
1234             }
1235 
WriteEndMember()1236             protected internal override void WriteEndMember()
1237             {
1238                 // We only want the untransformedNodes writer to contain our member contents, not the
1239                 // Start/End members, so don't write our closing EM
1240                 if (Writer.currentDepth != this.Depth)
1241                 {
1242                     base.WriteEndMember();
1243                 }
1244             }
1245 
Complete()1246             protected internal override void Complete()
1247             {
1248                 if (this.HasUntransformedChildren)
1249                 {
1250                     // Some ActivityPropertyReferences couldn't be transformed to properties. Leave them unchanged.
1251                     this.Parent.SetUntransformedPropertyReferences(this.originalStartMember, this.untransformedNodes);
1252                 }
1253             }
1254         }
1255 
1256         // <ActivityPropertyReference TargetProperty="Foo" SourceProperty="RootActivityProperty"> maps to
1257         // <SomeClass.Foo><PropertyReference x:TypeArguments='targetType' PropertyName='RootActivityProperty'/></SomeClass.Foo>
1258         class PropertyReferenceNode : BuilderXamlNode
1259         {
1260             XamlNodeQueue propertyReferenceNodes;
1261             PropertyReferencesNode parent;
1262             string sourceProperty;
1263             string targetProperty;
1264             bool inSourceProperty;
1265             bool inTargetProperty;
1266 
PropertyReferenceNode(ActivityBuilderXamlWriter writer, PropertyReferencesNode parent)1267             public PropertyReferenceNode(ActivityBuilderXamlWriter writer, PropertyReferencesNode parent)
1268                 : base(writer)
1269             {
1270                 this.propertyReferenceNodes = new XamlNodeQueue(writer.SchemaContext);
1271                 this.parent = parent;
1272 
1273                 // save the untransformed output in case we're not able to perform the transformation
1274                 base.CurrentWriter = this.propertyReferenceNodes.Writer;
1275             }
1276 
WriteStartMember(XamlMember xamlMember)1277             protected internal override void WriteStartMember(XamlMember xamlMember)
1278             {
1279                 if (Writer.currentDepth == this.Depth + 1 // SM
1280                     && xamlMember.DeclaringType == Writer.activityPropertyReferenceXamlType)
1281                 {
1282                     if (xamlMember.Name == "SourceProperty")
1283                     {
1284                         this.inSourceProperty = true;
1285                     }
1286                     else if (xamlMember.Name == "TargetProperty")
1287                     {
1288                         this.inTargetProperty = true;
1289                     }
1290                 }
1291                 base.WriteStartMember(xamlMember); // save output just in case
1292             }
1293 
WriteValue(object value)1294             protected internal override void WriteValue(object value)
1295             {
1296                 if (this.inSourceProperty)
1297                 {
1298                     this.sourceProperty = (string)value;
1299                 }
1300                 else if (this.inTargetProperty)
1301                 {
1302                     this.targetProperty = (string)value;
1303                 }
1304                 base.WriteValue(value); // save output just in case
1305             }
1306 
WriteEndMember()1307             protected internal override void WriteEndMember()
1308             {
1309                 if (Writer.currentDepth == this.Depth + 1)
1310                 {
1311                     this.inSourceProperty = false;
1312                     this.inTargetProperty = false;
1313                 }
1314                 base.WriteEndMember(); // save output just in case
1315             }
1316 
Complete()1317             protected internal override void Complete()
1318             {
1319                 if (this.targetProperty == null)
1320                 {
1321                     // can't transform to <Foo.></Foo.>, dump original nodes <ActivityBuilder.PropertyReference(s) .../>
1322                     this.parent.HasUntransformedChildren = true;
1323                     this.parent.UntransformedNodesWriter.WriteStartObject(Writer.activityPropertyReferenceXamlType);
1324                     XamlServices.Transform(this.propertyReferenceNodes.Reader, this.parent.UntransformedNodesWriter, false);
1325                 }
1326                 else
1327                 {
1328                     ActivityPropertyReference propertyReference = new ActivityPropertyReference
1329                     {
1330                         SourceProperty = this.sourceProperty,
1331                         TargetProperty = this.targetProperty
1332                     };
1333                     parent.Parent.AddPropertyReference(propertyReference);
1334                 }
1335             }
1336         }
1337     }
1338 }
1339