/// <summary> /// Copy fields from BamlReaderWrapper to the corresponding fields of BamlNodeData. /// </summary> /// <param name="reader">Source BamlReaderWrapper</param> /// <param name="nodeData">Destination nodeData where fields are to be copied.</param> private static void PopulateBamlNodeData(BamlReaderWrapper reader, BamlNodeData nodeData) { nodeData.NodeType = reader.NodeType; nodeData.AssemblyName = reader.AssemblyName; nodeData.Prefix = reader.Prefix; nodeData.XmlNamespace = reader.XmlNamespace; nodeData.ClrNamespace = reader.ClrNamespace; nodeData.Name = reader.Name; nodeData.LocalName = reader.LocalName; nodeData.Value = reader.Value; nodeData.IsInjected = reader.IsInjected; nodeData.CreateUsingTypeConverter = reader.CreateUsingTypeConverter; nodeData.TypeConverterAssemblyName = reader.TypeConverterAssemblyName; nodeData.TypeConverterName = reader.TypeConverterName; nodeData.AttributeUsage = reader.AttributeUsage; }
/// <summary> /// A function that compares the actual field values of a BamlNodeData with those expected, /// and throws an exception at the first mis-match. /// </summary> /// <param name="actualData">Actual data</param> /// <param name="expectedData">Expected data</param> private static void CompareBamlNodes(BamlNodeData actualData, BamlNodeData expectedData) { // Compare Name if (expectedData.Name != actualData.Name) { throw new Exception("Field different from expected in Baml node type " + actualData.NodeType + "\n" + " Name (expected): " + expectedData.Name + "\n" + " Name (found): " + actualData.Name); } // Compare LocalName if (expectedData.LocalName != actualData.LocalName) { throw new Exception("Field different from expected in Baml node type " + actualData.NodeType + "\n" + " LocalName (expected): " + expectedData.LocalName + "\n" + " LocalName (found): " + actualData.LocalName); } // Compare ClrNamespace if (expectedData.ClrNamespace != actualData.ClrNamespace) { throw new Exception("Field different from expected in Baml node type " + actualData.NodeType + "\n" + " ClrNamespace (expected): " + expectedData.ClrNamespace + "\n" + " ClrNamespace (found): " + actualData.ClrNamespace); } // Compare XmlNamespace if (expectedData.XmlNamespace != actualData.XmlNamespace) { throw new Exception("Field different from expected in Baml node type " + actualData.NodeType + "\n" + " XmlNamespace (expected): " + expectedData.XmlNamespace + "\n" + " XmlNamespace (found): " + actualData.XmlNamespace); } // Compare AssemblyName if (expectedData.AssemblyName != actualData.AssemblyName) { throw new Exception("Field different from expected in Baml node type " + actualData.NodeType + "\n" + " AssemblyName (expected): " + expectedData.AssemblyName + "\n" + " AssemblyName (found): " + actualData.AssemblyName); } // Compare Prefix if (expectedData.Prefix != actualData.Prefix) { throw new Exception("Field different from expected in Baml node type " + actualData.NodeType + "\n" + " Prefix (expected): " + expectedData.Prefix + "\n" + " Prefix (found): " + actualData.Prefix); } // Compare Value if (expectedData.Value != actualData.Value) { throw new Exception("Field different from expected in Baml node type " + actualData.NodeType + "\n" + " Value (expected): " + expectedData.Value + "\n" + " Value (found): " + actualData.Value); } }
/// <summary> /// Reads an existing BAML using BamlReaderWrapper, allows editing of the contents /// and writes the (possibly modified) contents to a new BAML using BamlWriterWrapper. /// /// After each node is read from the BamlReaderWrapper, it calls the given callback function, /// passing the data in the node just read. The callback function can then modify the /// data. The callback function is also passed the BamlWriterWrapper, so that it can add /// nodes, if it wants. /// /// The callback function is supposed to return a BamlNodeAction, specifiying /// whether the current BAML node should be written to the output BAML file. /// By returning BamlNodeAction.Skip, the callback function can specify to delete nodes. /// /// If the callback function returns BamlNodeAction.Continue, then the (possibly modified) /// contents will be written into the new BAML. /// </summary> /// <param name="bamlIn">Path of the Original BAML</param> /// <param name="bamlOut">Path of the BAML to be written</param> /// <param name="callback">EditBamlNode callback. Can be null.</param> public static void EditBaml(Stream bamlIn, Stream bamlOut, BamlNodeCallback callback) { BamlReaderWrapper reader = new BamlReaderWrapper(bamlIn); BamlWriterWrapper writer = new BamlWriterWrapper(bamlOut); // Stack for verifying matching BAML nodes Stack matchingStack = new Stack(); BamlNodeData expectedData; // Default action, in case there is no callback specified. BamlNodeAction action = BamlNodeAction.Continue; // Go through the input BAML, reading different types of records and // writing them to the output BAML while (reader.Read()) { // Copy the significant fields from BamlReaderWrapper to BamlNodeData // This nodeData can be then passed to a BamlNodeCallback delegate BamlNodeData nodeData = new BamlNodeData(); PopulateBamlNodeData(reader, nodeData); // Make another copy for pushing to stack, in case of those nodes // which we are checking for node matching // We cannot work with the previous copy, since that may be modified by the // callback. BamlNodeData stackData = new BamlNodeData(); PopulateBamlNodeData(reader, stackData); switch (reader.NodeType) { case "StartDocument": matchingStack.Push(stackData); if (callback != null) { action = callback(nodeData, writer); } if (BamlNodeAction.Continue == action) { writer.WriteStartDocument(); } break; case "EndDocument": // Test for matching BAML nodes. // Try to match the data with StartDocument's data if (matchingStack.Count <= 0) { throw new Exception("Unmatched BAML nodes found"); } expectedData = matchingStack.Pop() as BamlNodeData; if (expectedData.NodeType != "StartDocument") { throw new Exception("Unmatched BAML nodes found"); } CompareBamlNodes(stackData, expectedData); if (callback != null) { action = callback(nodeData, writer); } if (BamlNodeAction.Continue == action) { writer.WriteEndDocument(); } break; case "StartElement": matchingStack.Push(stackData); if (callback != null) { action = callback(nodeData, writer); } if (BamlNodeAction.Continue == action) { writer.WriteStartElement(nodeData.AssemblyName, nodeData.Name, nodeData.IsInjected, nodeData.CreateUsingTypeConverter); } break; case "EndElement": // Test for matching BAML nodes. // Try to match the data with StartElement's data if (matchingStack.Count <= 0) { throw new Exception("Unmatched BAML nodes found"); } expectedData = matchingStack.Pop() as BamlNodeData; if (expectedData.NodeType != "StartElement") { throw new Exception("Unmatched BAML nodes found"); } CompareBamlNodes(stackData, expectedData); if (callback != null) { action = callback(nodeData, writer); } if (BamlNodeAction.Continue == action) { writer.WriteEndElement(); } break; case "StartConstructor": matchingStack.Push(stackData); if (callback != null) { action = callback(nodeData, writer); } if (BamlNodeAction.Continue == action) { writer.WriteStartConstructor(); } break; case "EndConstructor": // Test for matching BAML nodes. // Try to match the data with StartConstructor's data if (matchingStack.Count <= 0) { throw new Exception("Unmatched BAML nodes found"); } expectedData = matchingStack.Pop() as BamlNodeData; if (expectedData.NodeType != "StartConstructor") { throw new Exception("Unmatched BAML nodes found"); } CompareBamlNodes(stackData, expectedData); if (callback != null) { action = callback(nodeData, writer); } if (BamlNodeAction.Continue == action) { writer.WriteEndConstructor(); } break; case "StartComplexProperty": matchingStack.Push(stackData); if (callback != null) { action = callback(nodeData, writer); } if (BamlNodeAction.Continue == action) { writer.WriteStartComplexProperty(nodeData.AssemblyName, nodeData.Name.Substring(0, nodeData.Name.LastIndexOf('.')), nodeData.LocalName); } break; case "EndComplexProperty": // Test for matching BAML nodes. // Try to match the data with StartComplexProperty's data if (matchingStack.Count <= 0) { throw new Exception("Unmatched BAML nodes found"); } expectedData = matchingStack.Pop() as BamlNodeData; if (expectedData.NodeType != "StartComplexProperty") { throw new Exception("Unmatched BAML nodes found"); } CompareBamlNodes(stackData, expectedData); if (callback != null) { action = callback(nodeData, writer); } if (BamlNodeAction.Continue == action) { writer.WriteEndComplexProperty(); } break; case "LiteralContent": if (callback != null) { action = callback(nodeData, writer); } if (BamlNodeAction.Continue == action) { writer.WriteLiteralContent(nodeData.Value); } break; case "Text": if (callback != null) { action = callback(nodeData, writer); } if (BamlNodeAction.Continue == action) { writer.WriteText(nodeData.Value, nodeData.TypeConverterAssemblyName, nodeData.TypeConverterName); } break; case "RoutedEvent": if (callback != null) { action = callback(nodeData, writer); } if (BamlNodeAction.Continue == action) { writer.WriteRoutedEvent(nodeData.AssemblyName, nodeData.Name.Substring(0, nodeData.Name.LastIndexOf('.')), nodeData.LocalName, nodeData.Value); } break; case "Event": if (callback != null) { action = callback(nodeData, writer); } if (BamlNodeAction.Continue == action) { writer.WriteEvent(nodeData.LocalName, nodeData.Value); } break; case "IncludeReference": if (callback != null) { action = callback(nodeData, writer); } break; case "PIMapping": if (callback != null) { action = callback(nodeData, writer); } if (BamlNodeAction.Continue == action) { writer.WritePIMapping(nodeData.XmlNamespace, nodeData.ClrNamespace, nodeData.AssemblyName); } break; default: throw new Exception("Unexpected NodeType read from BamlReaderWrapper"); } // Property nodes are not provided by BamlReaderWrapper when Read() is called // We need to go through them separately. if (reader.HasProperties) { reader.MoveToFirstProperty(); do { // This has to be present in this loop, because reader values // keep changing as we move thru the properties PopulateBamlNodeData(reader, nodeData); if (reader.NodeType == "Property") { if (callback != null) { action = callback(nodeData, writer); } if (BamlNodeAction.Continue == action) { writer.WriteProperty(nodeData.AssemblyName, nodeData.Name.Substring(0, nodeData.Name.LastIndexOf('.')), nodeData.LocalName, nodeData.Value, nodeData.AttributeUsage); } } else if (reader.NodeType == "ContentProperty") { if (callback != null) { action = callback(nodeData, writer); } if (BamlNodeAction.Continue == action) { writer.WriteContentProperty(nodeData.AssemblyName, nodeData.Name.Substring(0, nodeData.Name.LastIndexOf('.')), nodeData.LocalName); } } else if (reader.NodeType == "DefAttribute") { if (callback != null) { action = callback(nodeData, writer); } if (BamlNodeAction.Continue == action) { writer.WriteDefAttribute(nodeData.Name, nodeData.Value); } } else if (reader.NodeType == "XmlnsProperty") { if (callback != null) { action = callback(nodeData, writer); } if (BamlNodeAction.Continue == action) { writer.WriteXmlnsProperty(nodeData.LocalName, nodeData.Value); } } else if (reader.NodeType == "ConnectionId") { if (callback != null) { action = callback(nodeData, writer); } if (BamlNodeAction.Continue == action) { writer.WriteConnectionId(Int32.Parse(nodeData.Value)); } } else { throw new Exception("Unexpected NodeType read from BamlReaderWrapper while trying to read properties on an element"); } } while (reader.MoveToNextProperty()); } } // Check that matching stack is empty if (matchingStack.Count != 0) { throw new Exception("Unmatched BAML nodes found"); } }