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