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