Code:
/ Dotnetfx_Win7_3.5.1 / Dotnetfx_Win7_3.5.1 / 3.5.1 / DEVDIV / depot / DevDiv / releases / Orcas / NetFXw7 / wpf / src / Framework / System / Windows / Markup / BamlWriter.cs / 1 / BamlWriter.cs
/****************************************************************************\ * * File: BamlWriter.cs * * Purpose: Public api for writing baml records to a stream * * History: * 10/15/03: peterost Created * * Copyright (C) 2002 by Microsoft Corporation. All rights reserved. * \***************************************************************************/ using System; using System.Xml; using System.IO; using System.Windows; using System.Text; using System.Collections; using System.ComponentModel; using MS.Internal.Utility; using MS.Internal; using System.Diagnostics; using System.Reflection; using System.Windows.Controls; using System.Windows.Documents; using System.Windows.Threading; using MS.Utility; namespace System.Windows.Markup { ////// Writes BAML records to Stream and exposes an XmlWriter-liker interface for BAML /// //// This code should always be transparent. Meaning you should never add // SecurityCritical to this section of the code. // internal class BamlWriter : IParserHelper { #region Constructor ////// Create a BamlWriter on the passed stream. The stream must be writable. /// public BamlWriter( Stream stream) { if (null == stream) { throw new ArgumentNullException( "stream" ); } if (!stream.CanWrite) { throw new ArgumentException(SR.Get(SRID.BamlWriterBadStream)); } _parserContext = new ParserContext(); if (null == _parserContext.XamlTypeMapper) { _parserContext.XamlTypeMapper = new BamlWriterXamlTypeMapper(XmlParserDefaults.GetDefaultAssemblyNames(), XmlParserDefaults.GetDefaultNamespaceMaps()); } _xamlTypeMapper = _parserContext.XamlTypeMapper; _bamlRecordWriter = new BamlRecordWriter(stream, _parserContext, true); _startDocumentWritten = false; _depth = 0; _closed = false; _nodeTypeStack = new ParserStack(); _assemblies = new Hashtable(7); _extensionParser = new MarkupExtensionParser((IParserHelper)this, _parserContext); _markupExtensionNodes = new ArrayList(); } #endregion Constructor #region Close ////// Close the underlying stream and terminate write operations. /// ////// Once Close() is called, the BamlWriter cannot be used again and all /// subsequent calls to BamlWriter will fail. /// public void Close() { _bamlRecordWriter.BamlStream.Close(); _closed = true; } #endregion Close #region IParserHelper string IParserHelper.LookupNamespace(string prefix) { return _parserContext.XmlnsDictionary[prefix]; } bool IParserHelper.GetElementType( bool extensionFirst, string localName, string namespaceURI, ref string assemblyName, ref string typeFullName, ref Type baseType, ref Type serializerType) { bool result = false; assemblyName = string.Empty; typeFullName = string.Empty; serializerType = null; baseType = null; // if no namespaceURI or local name don't bother if (null == namespaceURI || null == localName) { return false; } TypeAndSerializer typeAndSerializer = _xamlTypeMapper.GetTypeAndSerializer(namespaceURI, localName, null); // If the normal type resolution fails, try the name with "Extension" added // so that we'll find MarkupExtension subclasses. if (typeAndSerializer == null) { typeAndSerializer = _xamlTypeMapper.GetTypeAndSerializer(namespaceURI, localName + "Extension", null); } if (typeAndSerializer != null && typeAndSerializer.ObjectType != null) { serializerType = typeAndSerializer.SerializerType; baseType = typeAndSerializer.ObjectType; typeFullName = baseType.FullName; assemblyName = baseType.Assembly.FullName; result = true; Debug.Assert(null != assemblyName,"assembly name returned from GetBaseElement is null"); Debug.Assert(null != typeFullName,"Type name returned from GetBaseElement is null"); } return result; } bool IParserHelper.CanResolveLocalAssemblies() { return false; } #endregion IParserHelper #region Record Writing ////// Write the start of document record, giving the baml version string /// ////// This must be the first call made when creating a new baml file. This /// is needed to specify the start of the document and baml version. /// public void WriteStartDocument() { if (_closed) { throw new InvalidOperationException(SR.Get(SRID.BamlWriterClosed)); } if (_startDocumentWritten) { throw new InvalidOperationException(SR.Get(SRID.BamlWriterStartDoc)); } XamlDocumentStartNode node = new XamlDocumentStartNode(0,0,_depth); _bamlRecordWriter.WriteDocumentStart(node); _startDocumentWritten = true; Push(BamlRecordType.DocumentStart); } ////// Write the end of document record. /// ////// This must be the last call made when creating a new baml file. /// public void WriteEndDocument() { VerifyEndTagState(BamlRecordType.DocumentStart, BamlRecordType.DocumentEnd); XamlDocumentEndNode node = new XamlDocumentEndNode(0,0,_depth); _bamlRecordWriter.WriteDocumentEnd(node); } ////// Write the connection Id record. /// ////// A record that contains the connection Id of the current element /// for hooking up IDs and events. /// public void WriteConnectionId(Int32 connectionId) { VerifyWriteState(); _bamlRecordWriter.WriteConnectionId(connectionId); } ////// Write the start of element record. /// ////// An element start marks the beginning of an object that exists /// in a tree structure. This may the contents of a complex property, /// or an element in the logical tree. /// public void WriteStartElement( string assemblyName, string typeFullName, bool isInjected, bool useTypeConverter) { VerifyWriteState(); _dpProperty = null; _parserContext.PushScope(); ProcessMarkupExtensionNodes(); Type elementType = GetType(assemblyName, typeFullName); Type serializerType = _xamlTypeMapper.GetXamlSerializerForType(elementType); Push(BamlRecordType.ElementStart, elementType); XamlElementStartNode node = new XamlElementStartNode( 0, 0, _depth++, assemblyName, typeFullName, elementType, serializerType); node.IsInjected = isInjected; node.CreateUsingTypeConverter = useTypeConverter; _bamlRecordWriter.WriteElementStart(node); } ////// Write the end of element record. /// ////// An element end marks the ending of an object that exists /// in a tree structure. This may the contents of a complex property, /// or an element in the logical tree. /// public void WriteEndElement() { VerifyEndTagState(BamlRecordType.ElementStart, BamlRecordType.ElementEnd); ProcessMarkupExtensionNodes(); XamlElementEndNode node = new XamlElementEndNode( 0, 0, --_depth); _bamlRecordWriter.WriteElementEnd(node); _parserContext.PopScope(); } ////// Write the start of constructor section that follows the start of an element. /// public void WriteStartConstructor() { VerifyWriteState(); Push(BamlRecordType.ConstructorParametersStart); XamlConstructorParametersStartNode node = new XamlConstructorParametersStartNode( 0, 0, --_depth); _bamlRecordWriter.WriteConstructorParametersStart(node); } ////// Write the end of constructor section that follows the start of an element. /// public void WriteEndConstructor() { VerifyEndTagState(BamlRecordType.ConstructorParametersStart, BamlRecordType.ConstructorParametersEnd); XamlConstructorParametersEndNode node = new XamlConstructorParametersEndNode( 0, 0, --_depth); _bamlRecordWriter.WriteConstructorParametersEnd(node); } ////// Write simple property information to baml. /// ////// If the type of this property supports /// custom serialization using a XamlSerializer and custom serialization /// is chosen, then write out a /// Custom serialization record. Note that for custom serialization /// to work, the assembly that contains the property's type must be /// loaded. If custom serialization is not chosen, then /// write out a 'normal' record, which will cause type conversion to happen /// at load time from the stored string. /// public void WriteProperty( string assemblyName, string ownerTypeFullName, string propName, string value, BamlAttributeUsage propUsage) { VerifyWriteState(); BamlRecordType parentType = PeekRecordType(); if (parentType != BamlRecordType.ElementStart) { throw new InvalidOperationException(SR.Get(SRID.BamlWriterNoInElement, "WriteProperty", parentType.ToString())); } object dpOrPi; Type declaringType; GetDpOrPi(assemblyName, ownerTypeFullName, propName, out dpOrPi, out declaringType); // Check if the value is a MarkupExtension. If so it must be expanded into // a series of baml records. Otherwise just write out the property. AttributeData data = _extensionParser.IsMarkupExtensionAttribute( declaringType, propName, ref value, 0, // No line numbers for baml 0, 0, dpOrPi); if (data == null) { XamlPropertyNode propNode = new XamlPropertyNode( 0, 0, _depth, dpOrPi, assemblyName, ownerTypeFullName, propName, value, propUsage, false); Type propType = XamlTypeMapper.GetPropertyType(dpOrPi); if (propType == typeof(DependencyProperty)) { Type ownerType = null; _dpProperty = XamlTypeMapper.ParsePropertyName(_parserContext, value, ref ownerType); if (_bamlRecordWriter != null && _dpProperty != null) { short typeId; short propertyId = _parserContext.MapTable.GetAttributeOrTypeId(_bamlRecordWriter.BinaryWriter, ownerType, _dpProperty.Name, out typeId); if (propertyId < 0) { propNode.ValueId = propertyId; propNode.MemberName = null; } else { propNode.ValueId = typeId; propNode.MemberName = _dpProperty.Name; } } } else if (_dpProperty != null) { propNode.ValuePropertyType = _dpProperty.PropertyType; propNode.ValuePropertyMember = _dpProperty; propNode.ValuePropertyName = _dpProperty.Name; propNode.ValueDeclaringType = _dpProperty.OwnerType; string propAssemblyName = _dpProperty.OwnerType.Assembly.FullName; _dpProperty = null; } _bamlRecordWriter.WriteProperty(propNode); } else { if (data.IsSimple) { if (data.IsTypeExtension) { Type typeValue = _xamlTypeMapper.GetTypeFromBaseString(data.Args, _parserContext, true); Debug.Assert(typeValue != null); XamlPropertyWithTypeNode xamlPropertyWithTypeNode = new XamlPropertyWithTypeNode(0, 0, _depth, dpOrPi, assemblyName, ownerTypeFullName, propName, typeValue.FullName, typeValue.Assembly.FullName, typeValue, string.Empty, string.Empty); _bamlRecordWriter.WritePropertyWithType(xamlPropertyWithTypeNode); } else { XamlPropertyWithExtensionNode xamlPropertyWithExtensionNode = new XamlPropertyWithExtensionNode(0, 0, _depth, dpOrPi, assemblyName, ownerTypeFullName, propName, data.Args, data.ExtensionTypeId, data.IsValueNestedExtension, data.IsValueTypeExtension); _bamlRecordWriter.WritePropertyWithExtension(xamlPropertyWithExtensionNode); } } else { _extensionParser.CompileAttribute( _markupExtensionNodes, data); } } } ////// Write Content Property Record. /// public void WriteContentProperty( string assemblyName, string ownerTypeFullName, string propName ) { object dpOrPi; Type declaringType; GetDpOrPi(assemblyName, ownerTypeFullName, propName, out dpOrPi, out declaringType); XamlContentPropertyNode CpaNode = new XamlContentPropertyNode( 0, 0, _depth, dpOrPi, assemblyName, ownerTypeFullName, propName); _bamlRecordWriter.WriteContentProperty( CpaNode ); } ////// Write xml namespace declaration information to baml. /// ////// This is used for setting the default namespace, or for mapping /// a namespace prefix (or localName) to a namespace. If prefix is /// an empty string, then this sets the default namespace. /// public void WriteXmlnsProperty( string localName, string xmlNamespace) { VerifyWriteState(); BamlRecordType parentType = PeekRecordType(); if (parentType != BamlRecordType.ElementStart && parentType != BamlRecordType.PropertyComplexStart && parentType != BamlRecordType.PropertyArrayStart && parentType != BamlRecordType.PropertyIListStart && parentType != BamlRecordType.PropertyIDictionaryStart) { throw new InvalidOperationException(SR.Get(SRID.BamlWriterBadXmlns, "WriteXmlnsProperty", parentType.ToString())); } XamlXmlnsPropertyNode xmlnsNode = new XamlXmlnsPropertyNode( 0, 0, _depth, localName, xmlNamespace); _bamlRecordWriter.WriteNamespacePrefix(xmlnsNode); _parserContext.XmlnsDictionary[localName] = xmlNamespace; } ////// Write an attribute in the Definition namespace. /// ////// This is really a processing directive, rather than an actual property. /// It is used to define keys for dictionaries, resource references, /// language declarations, base class names for tags, etc. /// public void WriteDefAttribute( string name, string value) { VerifyWriteState(); BamlRecordType parentType = PeekRecordType(); if (parentType != BamlRecordType.ElementStart && name != "Uid" ) // Parser's supposed to ignore x:Uid everywhere { throw new InvalidOperationException(SR.Get(SRID.BamlWriterNoInElement, "WriteDefAttribute", parentType.ToString())); } else if (name == XamlReaderHelper.DefinitionName) { // Check if this is a MarkupExtension, and if so expand it in xaml so that // it represents a key tree. DefAttributeData data = _extensionParser.IsMarkupExtensionDefAttribute( PeekElementType(), ref value, 0, 0, 0); if (data != null) { if (name != XamlReaderHelper.DefinitionName) { data.IsSimple = false; } if (data.IsSimple) { // If the MarkupExtension does not expand out into a complex property // subtree, but can be handled inline with other properties, then // process it immediately in place of the normal property int colonIndex = data.Args.IndexOf(':'); string prefix = string.Empty; string typeName = data.Args; if (colonIndex > 0) { prefix = data.Args.Substring(0, colonIndex); typeName = data.Args.Substring(colonIndex+1); } string valueNamespaceURI = _parserContext.XmlnsDictionary[prefix]; string valueAssemblyName = string.Empty; string valueTypeFullName = string.Empty; Type valueElementType = null; Type valueSerializerType = null; bool resolvedTag = ((IParserHelper)this).GetElementType(false, typeName, valueNamespaceURI,ref valueAssemblyName, ref valueTypeFullName, ref valueElementType, ref valueSerializerType); // If we can't resolve a simple TypeExtension value at compile time, // then write it out as a normal type extension and wait until runtime // since the type may not be visible now. if (resolvedTag) { XamlDefAttributeKeyTypeNode defKeyNode = new XamlDefAttributeKeyTypeNode( 0, 0, _depth, valueTypeFullName, valueElementType.Assembly.FullName, valueElementType); _bamlRecordWriter.WriteDefAttributeKeyType(defKeyNode); } else { data.IsSimple = false; data.Args += "}"; } } if (!data.IsSimple) { _extensionParser.CompileDictionaryKey( _markupExtensionNodes, data); } return; } } XamlDefAttributeNode defNode = new XamlDefAttributeNode( 0, 0, _depth, name, value); _bamlRecordWriter.WriteDefAttribute(defNode); } ////// Write an attribute in the PresentationOptions namespace. /// ////// This is really a processing directive, rather than an actual property. /// It is used to define WPF-specific parsing options (e.g., PresentationOptions:Freeze) /// public void WritePresentationOptionsAttribute( string name, string value) { VerifyWriteState(); XamlPresentationOptionsAttributeNode defNode = new XamlPresentationOptionsAttributeNode( 0, 0, _depth, name, value); _bamlRecordWriter.WritePresentationOptionsAttribute(defNode); } ////// Write the start of a complex property /// ////// A complex property start marks the beginning of an object that exists /// in a property on an element in the tree. /// public void WriteStartComplexProperty( string assemblyName, string ownerTypeFullName, string propName) { VerifyWriteState(); _parserContext.PushScope(); ProcessMarkupExtensionNodes(); object dpOrPi; Type ownerType; Type propertyType = null; bool propertyCanWrite = true; GetDpOrPi(assemblyName, ownerTypeFullName, propName, out dpOrPi, out ownerType); if (dpOrPi != null) { propertyType = XamlTypeMapper.GetPropertyType(dpOrPi); PropertyInfo pi = dpOrPi as PropertyInfo; if (pi != null) { propertyCanWrite = pi.CanWrite; } else { DependencyProperty dp = dpOrPi as DependencyProperty; if (dp != null) { propertyCanWrite = !dp.ReadOnly; } } } // Based on the type of the property, we could write this as // a complex IList, Array, IDictionary or a regular complex property. // NOTE: The order this is checked in must the same order as the // XamlReaderHelper.CompileComplexProperty so that we have // the same property behavior (eg - if something implements // IDictionary and IList, it is treated as an IList) if (propertyType == null) { // Unknown complex properties are written out using the string // information passed. This applies to things like Set.Value // and other style related DPs Push(BamlRecordType.PropertyComplexStart); XamlPropertyComplexStartNode propertyStart = new XamlPropertyComplexStartNode( 0, 0, _depth++, null, assemblyName, ownerTypeFullName, propName); _bamlRecordWriter.WritePropertyComplexStart(propertyStart); } else { BamlRecordType recordType = BamlRecordManager.GetPropertyStartRecordType(propertyType, propertyCanWrite); Push(recordType); switch (recordType) { case BamlRecordType.PropertyArrayStart: { XamlPropertyArrayStartNode arrayStart = new XamlPropertyArrayStartNode( 0, 0, _depth++, dpOrPi, assemblyName, ownerTypeFullName, propName); _bamlRecordWriter.WritePropertyArrayStart(arrayStart); break; } case BamlRecordType.PropertyIDictionaryStart: { XamlPropertyIDictionaryStartNode dictionaryStart = new XamlPropertyIDictionaryStartNode( 0, 0, _depth++, dpOrPi, assemblyName, ownerTypeFullName, propName); _bamlRecordWriter.WritePropertyIDictionaryStart(dictionaryStart); break; } case BamlRecordType.PropertyIListStart: { XamlPropertyIListStartNode listStart = new XamlPropertyIListStartNode( 0, 0, _depth++, dpOrPi, assemblyName, ownerTypeFullName, propName); _bamlRecordWriter.WritePropertyIListStart(listStart); break; } default: // PropertyComplexStart { XamlPropertyComplexStartNode node = new XamlPropertyComplexStartNode( 0, 0, _depth++, dpOrPi, assemblyName, ownerTypeFullName, propName); _bamlRecordWriter.WritePropertyComplexStart(node); break; } } } } ////// Write the end of the complex property /// public void WriteEndComplexProperty() { VerifyWriteState(); // The type of end record written depends on what the start record // was. This is held on the _nodeTypeStack BamlRecordType startTagType = Pop(); switch (startTagType) { case BamlRecordType.PropertyArrayStart: XamlPropertyArrayEndNode arrayEnd = new XamlPropertyArrayEndNode( 0, 0, --_depth); _bamlRecordWriter.WritePropertyArrayEnd(arrayEnd); break; case BamlRecordType.PropertyIListStart: XamlPropertyIListEndNode listEnd = new XamlPropertyIListEndNode( 0, 0, --_depth); _bamlRecordWriter.WritePropertyIListEnd(listEnd); break; case BamlRecordType.PropertyIDictionaryStart: XamlPropertyIDictionaryEndNode dictionaryEnd = new XamlPropertyIDictionaryEndNode( 0, 0, --_depth); _bamlRecordWriter.WritePropertyIDictionaryEnd(dictionaryEnd); break; case BamlRecordType.PropertyComplexStart: XamlPropertyComplexEndNode complexEnd = new XamlPropertyComplexEndNode( 0, 0, --_depth); _bamlRecordWriter.WritePropertyComplexEnd(complexEnd); break; default: throw new InvalidOperationException( SR.Get(SRID.BamlWriterBadScope, startTagType.ToString(), BamlRecordType.PropertyComplexEnd.ToString())); } _parserContext.PopScope(); } ////// Write a literal content record to baml stream /// public void WriteLiteralContent( string contents) { VerifyWriteState(); ProcessMarkupExtensionNodes(); XamlLiteralContentNode literalContent = new XamlLiteralContentNode( 0, 0, _depth, contents); _bamlRecordWriter.WriteLiteralContent(literalContent); } ////// Write a mapping processing instruction to baml stream /// public void WritePIMapping( string xmlNamespace, string clrNamespace, string assemblyName) { VerifyWriteState(); ProcessMarkupExtensionNodes(); XamlPIMappingNode piMapping = new XamlPIMappingNode( 0, 0, _depth, xmlNamespace, clrNamespace, assemblyName); if (!_xamlTypeMapper.PITable.Contains(xmlNamespace)) { ClrNamespaceAssemblyPair mapping = new ClrNamespaceAssemblyPair(clrNamespace, assemblyName); _xamlTypeMapper.PITable.Add(xmlNamespace, mapping); } // Write it out anyway. It is redundant but we are being asked to write it so write it. // In the round trip case this will preserve the exact file data. _bamlRecordWriter.WritePIMapping(piMapping); } ////// Write text content into baml stream /// public void WriteText( string textContent, string typeConverterAssemblyName, string typeConverterName) { VerifyWriteState(); ProcessMarkupExtensionNodes(); Type typeConverter=null; if (!String.IsNullOrEmpty(typeConverterName)) { typeConverter = GetType(typeConverterAssemblyName, typeConverterName); } XamlTextNode textNode = new XamlTextNode( 0, 0, _depth, textContent, typeConverter); _bamlRecordWriter.WriteText(textNode); } ////// Write a routed event record to BAML. /// ////// The Avalon parser does not process routed event records itself and will /// throw an exception if it encounters a routed event record in BAML. It /// is included here for completeness and future expandability. /// public void WriteRoutedEvent( string assemblyName, string ownerTypeFullName, string eventIdName, string handlerName) { #if EVENTSUPPORT VerifyWriteState(); XamlRoutedEventNode eventNode = new XamlRoutedEventNode( 0, 0, _depth, null, assemblyName, ownerTypeFullName, eventIdName, handlerName); _bamlRecordWriter.WriteRoutedEvent(eventNode); #else throw new NotSupportedException(SR.Get(SRID.ParserBamlEvent, eventIdName)); #endif } ////// Write an event record to BAML. /// ////// The Avalon parser does not process event records itself and will /// throw an exception if it encounters an event record in BAML. It /// is included here for completeness and future expandability. /// public void WriteEvent( string eventName, string handlerName) { #if EVENTSUPPORT VerifyWriteState(); XamlClrEventNode eventNode = new XamlClrEventNode( 0, 0, _depth, eventName, null, handlerName); _bamlRecordWriter.WriteClrEvent(eventNode); #else throw new NotSupportedException(SR.Get(SRID.ParserBamlEvent, eventName)); #endif } #endregion Record Writing #region Internal Methods /***************************************************************************\ * * BamlWriter.ProcessMarkupExtensionNodes * * Write out baml records for all the xamlnodes contained in the buffered * markup extension list. * NOTE: This list must contain only xamlnodes that are known to be part of * MarkupExtensions. This is NOT a general method to handle all types * of xaml nodes. * \***************************************************************************/ private void ProcessMarkupExtensionNodes() { for (int i=0; i < _markupExtensionNodes.Count; i++) { XamlNode node = _markupExtensionNodes[i] as XamlNode; switch (node.TokenType) { case XamlNodeType.ElementStart: _bamlRecordWriter.WriteElementStart((XamlElementStartNode)node); break; case XamlNodeType.ElementEnd: _bamlRecordWriter.WriteElementEnd((XamlElementEndNode)node); break; case XamlNodeType.KeyElementStart: _bamlRecordWriter.WriteKeyElementStart((XamlKeyElementStartNode)node); break; case XamlNodeType.KeyElementEnd: _bamlRecordWriter.WriteKeyElementEnd((XamlKeyElementEndNode)node); break; case XamlNodeType.Property: _bamlRecordWriter.WriteProperty((XamlPropertyNode)node); break; case XamlNodeType.PropertyWithExtension: _bamlRecordWriter.WritePropertyWithExtension((XamlPropertyWithExtensionNode)node); break; case XamlNodeType.PropertyWithType: _bamlRecordWriter.WritePropertyWithType((XamlPropertyWithTypeNode)node); break; case XamlNodeType.PropertyComplexStart: _bamlRecordWriter.WritePropertyComplexStart((XamlPropertyComplexStartNode)node); break; case XamlNodeType.PropertyComplexEnd: _bamlRecordWriter.WritePropertyComplexEnd((XamlPropertyComplexEndNode)node); break; case XamlNodeType.Text: _bamlRecordWriter.WriteText((XamlTextNode)node); break; case XamlNodeType.EndAttributes: _bamlRecordWriter.WriteEndAttributes((XamlEndAttributesNode)node); break; case XamlNodeType.ConstructorParametersStart: _bamlRecordWriter.WriteConstructorParametersStart((XamlConstructorParametersStartNode)node); break; case XamlNodeType.ConstructorParametersEnd: _bamlRecordWriter.WriteConstructorParametersEnd((XamlConstructorParametersEndNode)node); break; default: throw new InvalidOperationException(SR.Get(SRID.BamlWriterUnknownMarkupExtension)); } } _markupExtensionNodes.Clear(); } /***************************************************************************\ * * BamlWriter.VerifyWriteState * * Verify that we are in a good state to perform a record write. Throw * appropriate exceptions if not. * \***************************************************************************/ private void VerifyWriteState() { if (_closed) { throw new InvalidOperationException(SR.Get(SRID.BamlWriterClosed)); } if (!_startDocumentWritten) { throw new InvalidOperationException(SR.Get(SRID.BamlWriterStartDoc)); } } /***************************************************************************\ * * BamlWriter.VerifyEndTagState * * Verify that we are in a good state to perform a record write and that * the xamlnodetype on the node type stack is of the expected type. This * is called when an end tag record is written * \***************************************************************************/ private void VerifyEndTagState( BamlRecordType expectedStartTag, BamlRecordType endTagBeingWritten) { VerifyWriteState(); BamlRecordType startTagState = Pop(); if (startTagState != expectedStartTag) { throw new InvalidOperationException(SR.Get(SRID.BamlWriterBadScope, startTagState.ToString(), endTagBeingWritten.ToString())); } } /****************************************************************************\ * * BamlWriter.GetAssembly * * Get the Assembly given a name. This uses the LoadWrapper to load the * assembly from the current directory. * NOTE: Assembly paths are not currently supported, but may be in the * future if the need arises. * \***************************************************************************/ private Assembly GetAssembly(string assemblyName) { Assembly assy = _assemblies[assemblyName] as Assembly; if (assy == null) { assy = ReflectionHelper.LoadAssembly(assemblyName, null); if (assy == null) { throw new ArgumentException(SR.Get(SRID.BamlWriterBadAssembly, assemblyName)); } else { _assemblies[assemblyName] = assy; } } return assy; } /***************************************************************************\ * * BamlWriter.GetType * * Get the Type given an assembly name where the type is declared and the * type's fully qualified name * \***************************************************************************/ private Type GetType( string assemblyName, string typeFullName) { // Get the assembly that contains the type of the element Assembly assembly = GetAssembly(assemblyName); // Now see if the type is declared in this assembly Type objectType = assembly.GetType(typeFullName); // Note that null objectType is allowed, since this may be something like // aelement in a style, which is mapped to a element tag return objectType; } /****************************************************************************\ * * BamlWriter.GetDpOrPi * * Get the DependencyProperty or the PropertyInfo that corresponds to the * passed property name on the passed owner type (or type where the * property is declared). * \***************************************************************************/ private object GetDpOrPi( Type ownerType, string propName) { // Now that we have the type, see if this is a DependencyProperty or // a PropertyInfo. Note that ownerType may be null for fake properties // like those associated with Styles. Note also that the property may // not resolve to a known DP. This is allowed for fake properties like // those contained in Styles, such as Set.Value. object dpOrPi = null; #if !PBTCOMPILER if (ownerType != null) { dpOrPi = DependencyProperty.FromName(propName, ownerType); if (dpOrPi == null) { dpOrPi = ownerType.GetProperty(propName, BindingFlags.Instance | BindingFlags.Public); } } #else if (ownerType != null) { dpOrPi = KnownTypes.Types[(int)KnownElements.DependencyProperty].InvokeMember("FromName", BindingFlags.InvokeMethod, null, null, new object[] {propName, ownerType} ); if (dpOrPi == null) { dpOrPi = ownerType.GetProperty(propName, BindingFlags.Instance | BindingFlags.Public); } } #endif return dpOrPi; } /****************************************************************************\ * * BamlWriter.GetDpOrPi * * Get the DependencyProperty or the PropertyInfo that corresponds to the * passed property name on the passed owner type name. This typename is * first resolved to a type. * \***************************************************************************/ private void GetDpOrPi( string assemblyName, string ownerTypeFullName, string propName, out object dpOrPi, out Type ownerType) { // If there is no valid owner or assembly, then we can't resolve the type, // so just return null. This can occur if we are writing 'fake' properties // that are used for things such as Style property triggers. if (assemblyName == string.Empty || ownerTypeFullName == string.Empty) { dpOrPi = null; ownerType = null; } else { ownerType = GetType(assemblyName, ownerTypeFullName); dpOrPi = GetDpOrPi(ownerType, propName); } } // Helper methods for dealing with the node stack // Push a new record type on the stack. If we have not generated an // end attributes write operation for the current item on the stack, and // that item is a start element, then now is the time to do it. private void Push(BamlRecordType recordType) { CheckEndAttributes(); _nodeTypeStack.Push(new WriteStackNode(recordType)); } private void Push(BamlRecordType recordType, Type elementType) { CheckEndAttributes(); _nodeTypeStack.Push(new WriteStackNode(recordType, elementType)); } // Pop an item off the node stack and return its type. private BamlRecordType Pop() { WriteStackNode stackNode = _nodeTypeStack.Pop() as WriteStackNode; Debug.Assert(stackNode != null); return stackNode.RecordType; } // Return the record type on the top of the stack private BamlRecordType PeekRecordType() { WriteStackNode stackNode = _nodeTypeStack.Peek() as WriteStackNode; Debug.Assert(stackNode != null); return stackNode.RecordType; } // Return the element type on the top of the stack private Type PeekElementType() { WriteStackNode stackNode = _nodeTypeStack.Peek() as WriteStackNode; Debug.Assert(stackNode != null); return stackNode.ElementType; } // Check if we have to insert an EndAttributes xaml node at the end // of an element start tag. private void CheckEndAttributes() { if (_nodeTypeStack.Count > 0) { WriteStackNode parentNode = _nodeTypeStack.Peek() as WriteStackNode; if (!parentNode.EndAttributesReached && parentNode.RecordType == BamlRecordType.ElementStart) { XamlEndAttributesNode node = new XamlEndAttributesNode( 0, 0, _depth, false); _bamlRecordWriter.WriteEndAttributes(node); } parentNode.EndAttributesReached = true; } } #endregion Internal Methods #region Data // Item pushed on a node stack to keep track for matching // end tags and for determining when the end of the // start tag has been reached for generating an end attribute write. private class WriteStackNode { public WriteStackNode( BamlRecordType recordType) { _recordType = recordType; _endAttributesReached = false; } public WriteStackNode( BamlRecordType recordType, Type elementType) : this(recordType) { _elementType = elementType; } public BamlRecordType RecordType { get { return _recordType; } } public bool EndAttributesReached { get { return _endAttributesReached; } set { _endAttributesReached = value; } } public Type ElementType { get { return _elementType; } } bool _endAttributesReached; BamlRecordType _recordType; Type _elementType; } // Writer that actually writes BamlRecords to a stream. BamlRecordWriter _bamlRecordWriter; // True if the DocumentStart record has been written to the stream. bool _startDocumentWritten; // The depth of the element tree, including complex properties int _depth; // True if Close() has been called. bool _closed; // If a custom property is of Type DependencyProperty, this is used to provide // info about the Type of value for such a property in order to write it out in // an optimized form by its custom serializer. DependencyProperty _dpProperty; // Stack of the type of nodes written to the baml stream. This is // used for end-tag matching and basic structure checking. This // contains WriteStackNode objects. ParserStack _nodeTypeStack; // Cache of assemblies that are needed for type resolutions when // doingIBamlSerialize Hashtable _assemblies; // XamlTypeMapper used by this writer XamlTypeMapper _xamlTypeMapper; // ParserContext for this writer ParserContext _parserContext; // The helper class that handles parsing of MarkupExtension values. MarkupExtensionParser _extensionParser; // Buffered XamlNodes that occur when a property with a MarkupExtension value // is written and expanded into a complex property subtree. ArrayList _markupExtensionNodes; #endregion Data } internal class BamlWriterXamlTypeMapper : XamlTypeMapper { internal BamlWriterXamlTypeMapper( string[] assemblyNames, NamespaceMapEntry[] namespaceMaps) : base(assemblyNames, namespaceMaps) { } /// /// Allows BamlWriter to allow access to legitimate internal types /// /// The internal type ////// Always Returns true by default, since this is used by localization /// sealed protected override bool AllowInternalType(Type type) { return true; } } } // File provided for Reference Use Only by Microsoft Corporation (c) 2007. // Copyright (c) Microsoft Corporation. All rights reserved. /****************************************************************************\ * * File: BamlWriter.cs * * Purpose: Public api for writing baml records to a stream * * History: * 10/15/03: peterost Created * * Copyright (C) 2002 by Microsoft Corporation. All rights reserved. * \***************************************************************************/ using System; using System.Xml; using System.IO; using System.Windows; using System.Text; using System.Collections; using System.ComponentModel; using MS.Internal.Utility; using MS.Internal; using System.Diagnostics; using System.Reflection; using System.Windows.Controls; using System.Windows.Documents; using System.Windows.Threading; using MS.Utility; namespace System.Windows.Markup { ////// Writes BAML records to Stream and exposes an XmlWriter-liker interface for BAML /// //// This code should always be transparent. Meaning you should never add // SecurityCritical to this section of the code. // internal class BamlWriter : IParserHelper { #region Constructor ////// Create a BamlWriter on the passed stream. The stream must be writable. /// public BamlWriter( Stream stream) { if (null == stream) { throw new ArgumentNullException( "stream" ); } if (!stream.CanWrite) { throw new ArgumentException(SR.Get(SRID.BamlWriterBadStream)); } _parserContext = new ParserContext(); if (null == _parserContext.XamlTypeMapper) { _parserContext.XamlTypeMapper = new BamlWriterXamlTypeMapper(XmlParserDefaults.GetDefaultAssemblyNames(), XmlParserDefaults.GetDefaultNamespaceMaps()); } _xamlTypeMapper = _parserContext.XamlTypeMapper; _bamlRecordWriter = new BamlRecordWriter(stream, _parserContext, true); _startDocumentWritten = false; _depth = 0; _closed = false; _nodeTypeStack = new ParserStack(); _assemblies = new Hashtable(7); _extensionParser = new MarkupExtensionParser((IParserHelper)this, _parserContext); _markupExtensionNodes = new ArrayList(); } #endregion Constructor #region Close ////// Close the underlying stream and terminate write operations. /// ////// Once Close() is called, the BamlWriter cannot be used again and all /// subsequent calls to BamlWriter will fail. /// public void Close() { _bamlRecordWriter.BamlStream.Close(); _closed = true; } #endregion Close #region IParserHelper string IParserHelper.LookupNamespace(string prefix) { return _parserContext.XmlnsDictionary[prefix]; } bool IParserHelper.GetElementType( bool extensionFirst, string localName, string namespaceURI, ref string assemblyName, ref string typeFullName, ref Type baseType, ref Type serializerType) { bool result = false; assemblyName = string.Empty; typeFullName = string.Empty; serializerType = null; baseType = null; // if no namespaceURI or local name don't bother if (null == namespaceURI || null == localName) { return false; } TypeAndSerializer typeAndSerializer = _xamlTypeMapper.GetTypeAndSerializer(namespaceURI, localName, null); // If the normal type resolution fails, try the name with "Extension" added // so that we'll find MarkupExtension subclasses. if (typeAndSerializer == null) { typeAndSerializer = _xamlTypeMapper.GetTypeAndSerializer(namespaceURI, localName + "Extension", null); } if (typeAndSerializer != null && typeAndSerializer.ObjectType != null) { serializerType = typeAndSerializer.SerializerType; baseType = typeAndSerializer.ObjectType; typeFullName = baseType.FullName; assemblyName = baseType.Assembly.FullName; result = true; Debug.Assert(null != assemblyName,"assembly name returned from GetBaseElement is null"); Debug.Assert(null != typeFullName,"Type name returned from GetBaseElement is null"); } return result; } bool IParserHelper.CanResolveLocalAssemblies() { return false; } #endregion IParserHelper #region Record Writing ////// Write the start of document record, giving the baml version string /// ////// This must be the first call made when creating a new baml file. This /// is needed to specify the start of the document and baml version. /// public void WriteStartDocument() { if (_closed) { throw new InvalidOperationException(SR.Get(SRID.BamlWriterClosed)); } if (_startDocumentWritten) { throw new InvalidOperationException(SR.Get(SRID.BamlWriterStartDoc)); } XamlDocumentStartNode node = new XamlDocumentStartNode(0,0,_depth); _bamlRecordWriter.WriteDocumentStart(node); _startDocumentWritten = true; Push(BamlRecordType.DocumentStart); } ////// Write the end of document record. /// ////// This must be the last call made when creating a new baml file. /// public void WriteEndDocument() { VerifyEndTagState(BamlRecordType.DocumentStart, BamlRecordType.DocumentEnd); XamlDocumentEndNode node = new XamlDocumentEndNode(0,0,_depth); _bamlRecordWriter.WriteDocumentEnd(node); } ////// Write the connection Id record. /// ////// A record that contains the connection Id of the current element /// for hooking up IDs and events. /// public void WriteConnectionId(Int32 connectionId) { VerifyWriteState(); _bamlRecordWriter.WriteConnectionId(connectionId); } ////// Write the start of element record. /// ////// An element start marks the beginning of an object that exists /// in a tree structure. This may the contents of a complex property, /// or an element in the logical tree. /// public void WriteStartElement( string assemblyName, string typeFullName, bool isInjected, bool useTypeConverter) { VerifyWriteState(); _dpProperty = null; _parserContext.PushScope(); ProcessMarkupExtensionNodes(); Type elementType = GetType(assemblyName, typeFullName); Type serializerType = _xamlTypeMapper.GetXamlSerializerForType(elementType); Push(BamlRecordType.ElementStart, elementType); XamlElementStartNode node = new XamlElementStartNode( 0, 0, _depth++, assemblyName, typeFullName, elementType, serializerType); node.IsInjected = isInjected; node.CreateUsingTypeConverter = useTypeConverter; _bamlRecordWriter.WriteElementStart(node); } ////// Write the end of element record. /// ////// An element end marks the ending of an object that exists /// in a tree structure. This may the contents of a complex property, /// or an element in the logical tree. /// public void WriteEndElement() { VerifyEndTagState(BamlRecordType.ElementStart, BamlRecordType.ElementEnd); ProcessMarkupExtensionNodes(); XamlElementEndNode node = new XamlElementEndNode( 0, 0, --_depth); _bamlRecordWriter.WriteElementEnd(node); _parserContext.PopScope(); } ////// Write the start of constructor section that follows the start of an element. /// public void WriteStartConstructor() { VerifyWriteState(); Push(BamlRecordType.ConstructorParametersStart); XamlConstructorParametersStartNode node = new XamlConstructorParametersStartNode( 0, 0, --_depth); _bamlRecordWriter.WriteConstructorParametersStart(node); } ////// Write the end of constructor section that follows the start of an element. /// public void WriteEndConstructor() { VerifyEndTagState(BamlRecordType.ConstructorParametersStart, BamlRecordType.ConstructorParametersEnd); XamlConstructorParametersEndNode node = new XamlConstructorParametersEndNode( 0, 0, --_depth); _bamlRecordWriter.WriteConstructorParametersEnd(node); } ////// Write simple property information to baml. /// ////// If the type of this property supports /// custom serialization using a XamlSerializer and custom serialization /// is chosen, then write out a /// Custom serialization record. Note that for custom serialization /// to work, the assembly that contains the property's type must be /// loaded. If custom serialization is not chosen, then /// write out a 'normal' record, which will cause type conversion to happen /// at load time from the stored string. /// public void WriteProperty( string assemblyName, string ownerTypeFullName, string propName, string value, BamlAttributeUsage propUsage) { VerifyWriteState(); BamlRecordType parentType = PeekRecordType(); if (parentType != BamlRecordType.ElementStart) { throw new InvalidOperationException(SR.Get(SRID.BamlWriterNoInElement, "WriteProperty", parentType.ToString())); } object dpOrPi; Type declaringType; GetDpOrPi(assemblyName, ownerTypeFullName, propName, out dpOrPi, out declaringType); // Check if the value is a MarkupExtension. If so it must be expanded into // a series of baml records. Otherwise just write out the property. AttributeData data = _extensionParser.IsMarkupExtensionAttribute( declaringType, propName, ref value, 0, // No line numbers for baml 0, 0, dpOrPi); if (data == null) { XamlPropertyNode propNode = new XamlPropertyNode( 0, 0, _depth, dpOrPi, assemblyName, ownerTypeFullName, propName, value, propUsage, false); Type propType = XamlTypeMapper.GetPropertyType(dpOrPi); if (propType == typeof(DependencyProperty)) { Type ownerType = null; _dpProperty = XamlTypeMapper.ParsePropertyName(_parserContext, value, ref ownerType); if (_bamlRecordWriter != null && _dpProperty != null) { short typeId; short propertyId = _parserContext.MapTable.GetAttributeOrTypeId(_bamlRecordWriter.BinaryWriter, ownerType, _dpProperty.Name, out typeId); if (propertyId < 0) { propNode.ValueId = propertyId; propNode.MemberName = null; } else { propNode.ValueId = typeId; propNode.MemberName = _dpProperty.Name; } } } else if (_dpProperty != null) { propNode.ValuePropertyType = _dpProperty.PropertyType; propNode.ValuePropertyMember = _dpProperty; propNode.ValuePropertyName = _dpProperty.Name; propNode.ValueDeclaringType = _dpProperty.OwnerType; string propAssemblyName = _dpProperty.OwnerType.Assembly.FullName; _dpProperty = null; } _bamlRecordWriter.WriteProperty(propNode); } else { if (data.IsSimple) { if (data.IsTypeExtension) { Type typeValue = _xamlTypeMapper.GetTypeFromBaseString(data.Args, _parserContext, true); Debug.Assert(typeValue != null); XamlPropertyWithTypeNode xamlPropertyWithTypeNode = new XamlPropertyWithTypeNode(0, 0, _depth, dpOrPi, assemblyName, ownerTypeFullName, propName, typeValue.FullName, typeValue.Assembly.FullName, typeValue, string.Empty, string.Empty); _bamlRecordWriter.WritePropertyWithType(xamlPropertyWithTypeNode); } else { XamlPropertyWithExtensionNode xamlPropertyWithExtensionNode = new XamlPropertyWithExtensionNode(0, 0, _depth, dpOrPi, assemblyName, ownerTypeFullName, propName, data.Args, data.ExtensionTypeId, data.IsValueNestedExtension, data.IsValueTypeExtension); _bamlRecordWriter.WritePropertyWithExtension(xamlPropertyWithExtensionNode); } } else { _extensionParser.CompileAttribute( _markupExtensionNodes, data); } } } ////// Write Content Property Record. /// public void WriteContentProperty( string assemblyName, string ownerTypeFullName, string propName ) { object dpOrPi; Type declaringType; GetDpOrPi(assemblyName, ownerTypeFullName, propName, out dpOrPi, out declaringType); XamlContentPropertyNode CpaNode = new XamlContentPropertyNode( 0, 0, _depth, dpOrPi, assemblyName, ownerTypeFullName, propName); _bamlRecordWriter.WriteContentProperty( CpaNode ); } ////// Write xml namespace declaration information to baml. /// ////// This is used for setting the default namespace, or for mapping /// a namespace prefix (or localName) to a namespace. If prefix is /// an empty string, then this sets the default namespace. /// public void WriteXmlnsProperty( string localName, string xmlNamespace) { VerifyWriteState(); BamlRecordType parentType = PeekRecordType(); if (parentType != BamlRecordType.ElementStart && parentType != BamlRecordType.PropertyComplexStart && parentType != BamlRecordType.PropertyArrayStart && parentType != BamlRecordType.PropertyIListStart && parentType != BamlRecordType.PropertyIDictionaryStart) { throw new InvalidOperationException(SR.Get(SRID.BamlWriterBadXmlns, "WriteXmlnsProperty", parentType.ToString())); } XamlXmlnsPropertyNode xmlnsNode = new XamlXmlnsPropertyNode( 0, 0, _depth, localName, xmlNamespace); _bamlRecordWriter.WriteNamespacePrefix(xmlnsNode); _parserContext.XmlnsDictionary[localName] = xmlNamespace; } ////// Write an attribute in the Definition namespace. /// ////// This is really a processing directive, rather than an actual property. /// It is used to define keys for dictionaries, resource references, /// language declarations, base class names for tags, etc. /// public void WriteDefAttribute( string name, string value) { VerifyWriteState(); BamlRecordType parentType = PeekRecordType(); if (parentType != BamlRecordType.ElementStart && name != "Uid" ) // Parser's supposed to ignore x:Uid everywhere { throw new InvalidOperationException(SR.Get(SRID.BamlWriterNoInElement, "WriteDefAttribute", parentType.ToString())); } else if (name == XamlReaderHelper.DefinitionName) { // Check if this is a MarkupExtension, and if so expand it in xaml so that // it represents a key tree. DefAttributeData data = _extensionParser.IsMarkupExtensionDefAttribute( PeekElementType(), ref value, 0, 0, 0); if (data != null) { if (name != XamlReaderHelper.DefinitionName) { data.IsSimple = false; } if (data.IsSimple) { // If the MarkupExtension does not expand out into a complex property // subtree, but can be handled inline with other properties, then // process it immediately in place of the normal property int colonIndex = data.Args.IndexOf(':'); string prefix = string.Empty; string typeName = data.Args; if (colonIndex > 0) { prefix = data.Args.Substring(0, colonIndex); typeName = data.Args.Substring(colonIndex+1); } string valueNamespaceURI = _parserContext.XmlnsDictionary[prefix]; string valueAssemblyName = string.Empty; string valueTypeFullName = string.Empty; Type valueElementType = null; Type valueSerializerType = null; bool resolvedTag = ((IParserHelper)this).GetElementType(false, typeName, valueNamespaceURI,ref valueAssemblyName, ref valueTypeFullName, ref valueElementType, ref valueSerializerType); // If we can't resolve a simple TypeExtension value at compile time, // then write it out as a normal type extension and wait until runtime // since the type may not be visible now. if (resolvedTag) { XamlDefAttributeKeyTypeNode defKeyNode = new XamlDefAttributeKeyTypeNode( 0, 0, _depth, valueTypeFullName, valueElementType.Assembly.FullName, valueElementType); _bamlRecordWriter.WriteDefAttributeKeyType(defKeyNode); } else { data.IsSimple = false; data.Args += "}"; } } if (!data.IsSimple) { _extensionParser.CompileDictionaryKey( _markupExtensionNodes, data); } return; } } XamlDefAttributeNode defNode = new XamlDefAttributeNode( 0, 0, _depth, name, value); _bamlRecordWriter.WriteDefAttribute(defNode); } ////// Write an attribute in the PresentationOptions namespace. /// ////// This is really a processing directive, rather than an actual property. /// It is used to define WPF-specific parsing options (e.g., PresentationOptions:Freeze) /// public void WritePresentationOptionsAttribute( string name, string value) { VerifyWriteState(); XamlPresentationOptionsAttributeNode defNode = new XamlPresentationOptionsAttributeNode( 0, 0, _depth, name, value); _bamlRecordWriter.WritePresentationOptionsAttribute(defNode); } ////// Write the start of a complex property /// ////// A complex property start marks the beginning of an object that exists /// in a property on an element in the tree. /// public void WriteStartComplexProperty( string assemblyName, string ownerTypeFullName, string propName) { VerifyWriteState(); _parserContext.PushScope(); ProcessMarkupExtensionNodes(); object dpOrPi; Type ownerType; Type propertyType = null; bool propertyCanWrite = true; GetDpOrPi(assemblyName, ownerTypeFullName, propName, out dpOrPi, out ownerType); if (dpOrPi != null) { propertyType = XamlTypeMapper.GetPropertyType(dpOrPi); PropertyInfo pi = dpOrPi as PropertyInfo; if (pi != null) { propertyCanWrite = pi.CanWrite; } else { DependencyProperty dp = dpOrPi as DependencyProperty; if (dp != null) { propertyCanWrite = !dp.ReadOnly; } } } // Based on the type of the property, we could write this as // a complex IList, Array, IDictionary or a regular complex property. // NOTE: The order this is checked in must the same order as the // XamlReaderHelper.CompileComplexProperty so that we have // the same property behavior (eg - if something implements // IDictionary and IList, it is treated as an IList) if (propertyType == null) { // Unknown complex properties are written out using the string // information passed. This applies to things like Set.Value // and other style related DPs Push(BamlRecordType.PropertyComplexStart); XamlPropertyComplexStartNode propertyStart = new XamlPropertyComplexStartNode( 0, 0, _depth++, null, assemblyName, ownerTypeFullName, propName); _bamlRecordWriter.WritePropertyComplexStart(propertyStart); } else { BamlRecordType recordType = BamlRecordManager.GetPropertyStartRecordType(propertyType, propertyCanWrite); Push(recordType); switch (recordType) { case BamlRecordType.PropertyArrayStart: { XamlPropertyArrayStartNode arrayStart = new XamlPropertyArrayStartNode( 0, 0, _depth++, dpOrPi, assemblyName, ownerTypeFullName, propName); _bamlRecordWriter.WritePropertyArrayStart(arrayStart); break; } case BamlRecordType.PropertyIDictionaryStart: { XamlPropertyIDictionaryStartNode dictionaryStart = new XamlPropertyIDictionaryStartNode( 0, 0, _depth++, dpOrPi, assemblyName, ownerTypeFullName, propName); _bamlRecordWriter.WritePropertyIDictionaryStart(dictionaryStart); break; } case BamlRecordType.PropertyIListStart: { XamlPropertyIListStartNode listStart = new XamlPropertyIListStartNode( 0, 0, _depth++, dpOrPi, assemblyName, ownerTypeFullName, propName); _bamlRecordWriter.WritePropertyIListStart(listStart); break; } default: // PropertyComplexStart { XamlPropertyComplexStartNode node = new XamlPropertyComplexStartNode( 0, 0, _depth++, dpOrPi, assemblyName, ownerTypeFullName, propName); _bamlRecordWriter.WritePropertyComplexStart(node); break; } } } } ////// Write the end of the complex property /// public void WriteEndComplexProperty() { VerifyWriteState(); // The type of end record written depends on what the start record // was. This is held on the _nodeTypeStack BamlRecordType startTagType = Pop(); switch (startTagType) { case BamlRecordType.PropertyArrayStart: XamlPropertyArrayEndNode arrayEnd = new XamlPropertyArrayEndNode( 0, 0, --_depth); _bamlRecordWriter.WritePropertyArrayEnd(arrayEnd); break; case BamlRecordType.PropertyIListStart: XamlPropertyIListEndNode listEnd = new XamlPropertyIListEndNode( 0, 0, --_depth); _bamlRecordWriter.WritePropertyIListEnd(listEnd); break; case BamlRecordType.PropertyIDictionaryStart: XamlPropertyIDictionaryEndNode dictionaryEnd = new XamlPropertyIDictionaryEndNode( 0, 0, --_depth); _bamlRecordWriter.WritePropertyIDictionaryEnd(dictionaryEnd); break; case BamlRecordType.PropertyComplexStart: XamlPropertyComplexEndNode complexEnd = new XamlPropertyComplexEndNode( 0, 0, --_depth); _bamlRecordWriter.WritePropertyComplexEnd(complexEnd); break; default: throw new InvalidOperationException( SR.Get(SRID.BamlWriterBadScope, startTagType.ToString(), BamlRecordType.PropertyComplexEnd.ToString())); } _parserContext.PopScope(); } ////// Write a literal content record to baml stream /// public void WriteLiteralContent( string contents) { VerifyWriteState(); ProcessMarkupExtensionNodes(); XamlLiteralContentNode literalContent = new XamlLiteralContentNode( 0, 0, _depth, contents); _bamlRecordWriter.WriteLiteralContent(literalContent); } ////// Write a mapping processing instruction to baml stream /// public void WritePIMapping( string xmlNamespace, string clrNamespace, string assemblyName) { VerifyWriteState(); ProcessMarkupExtensionNodes(); XamlPIMappingNode piMapping = new XamlPIMappingNode( 0, 0, _depth, xmlNamespace, clrNamespace, assemblyName); if (!_xamlTypeMapper.PITable.Contains(xmlNamespace)) { ClrNamespaceAssemblyPair mapping = new ClrNamespaceAssemblyPair(clrNamespace, assemblyName); _xamlTypeMapper.PITable.Add(xmlNamespace, mapping); } // Write it out anyway. It is redundant but we are being asked to write it so write it. // In the round trip case this will preserve the exact file data. _bamlRecordWriter.WritePIMapping(piMapping); } ////// Write text content into baml stream /// public void WriteText( string textContent, string typeConverterAssemblyName, string typeConverterName) { VerifyWriteState(); ProcessMarkupExtensionNodes(); Type typeConverter=null; if (!String.IsNullOrEmpty(typeConverterName)) { typeConverter = GetType(typeConverterAssemblyName, typeConverterName); } XamlTextNode textNode = new XamlTextNode( 0, 0, _depth, textContent, typeConverter); _bamlRecordWriter.WriteText(textNode); } ////// Write a routed event record to BAML. /// ////// The Avalon parser does not process routed event records itself and will /// throw an exception if it encounters a routed event record in BAML. It /// is included here for completeness and future expandability. /// public void WriteRoutedEvent( string assemblyName, string ownerTypeFullName, string eventIdName, string handlerName) { #if EVENTSUPPORT VerifyWriteState(); XamlRoutedEventNode eventNode = new XamlRoutedEventNode( 0, 0, _depth, null, assemblyName, ownerTypeFullName, eventIdName, handlerName); _bamlRecordWriter.WriteRoutedEvent(eventNode); #else throw new NotSupportedException(SR.Get(SRID.ParserBamlEvent, eventIdName)); #endif } ////// Write an event record to BAML. /// ////// The Avalon parser does not process event records itself and will /// throw an exception if it encounters an event record in BAML. It /// is included here for completeness and future expandability. /// public void WriteEvent( string eventName, string handlerName) { #if EVENTSUPPORT VerifyWriteState(); XamlClrEventNode eventNode = new XamlClrEventNode( 0, 0, _depth, eventName, null, handlerName); _bamlRecordWriter.WriteClrEvent(eventNode); #else throw new NotSupportedException(SR.Get(SRID.ParserBamlEvent, eventName)); #endif } #endregion Record Writing #region Internal Methods /***************************************************************************\ * * BamlWriter.ProcessMarkupExtensionNodes * * Write out baml records for all the xamlnodes contained in the buffered * markup extension list. * NOTE: This list must contain only xamlnodes that are known to be part of * MarkupExtensions. This is NOT a general method to handle all types * of xaml nodes. * \***************************************************************************/ private void ProcessMarkupExtensionNodes() { for (int i=0; i < _markupExtensionNodes.Count; i++) { XamlNode node = _markupExtensionNodes[i] as XamlNode; switch (node.TokenType) { case XamlNodeType.ElementStart: _bamlRecordWriter.WriteElementStart((XamlElementStartNode)node); break; case XamlNodeType.ElementEnd: _bamlRecordWriter.WriteElementEnd((XamlElementEndNode)node); break; case XamlNodeType.KeyElementStart: _bamlRecordWriter.WriteKeyElementStart((XamlKeyElementStartNode)node); break; case XamlNodeType.KeyElementEnd: _bamlRecordWriter.WriteKeyElementEnd((XamlKeyElementEndNode)node); break; case XamlNodeType.Property: _bamlRecordWriter.WriteProperty((XamlPropertyNode)node); break; case XamlNodeType.PropertyWithExtension: _bamlRecordWriter.WritePropertyWithExtension((XamlPropertyWithExtensionNode)node); break; case XamlNodeType.PropertyWithType: _bamlRecordWriter.WritePropertyWithType((XamlPropertyWithTypeNode)node); break; case XamlNodeType.PropertyComplexStart: _bamlRecordWriter.WritePropertyComplexStart((XamlPropertyComplexStartNode)node); break; case XamlNodeType.PropertyComplexEnd: _bamlRecordWriter.WritePropertyComplexEnd((XamlPropertyComplexEndNode)node); break; case XamlNodeType.Text: _bamlRecordWriter.WriteText((XamlTextNode)node); break; case XamlNodeType.EndAttributes: _bamlRecordWriter.WriteEndAttributes((XamlEndAttributesNode)node); break; case XamlNodeType.ConstructorParametersStart: _bamlRecordWriter.WriteConstructorParametersStart((XamlConstructorParametersStartNode)node); break; case XamlNodeType.ConstructorParametersEnd: _bamlRecordWriter.WriteConstructorParametersEnd((XamlConstructorParametersEndNode)node); break; default: throw new InvalidOperationException(SR.Get(SRID.BamlWriterUnknownMarkupExtension)); } } _markupExtensionNodes.Clear(); } /***************************************************************************\ * * BamlWriter.VerifyWriteState * * Verify that we are in a good state to perform a record write. Throw * appropriate exceptions if not. * \***************************************************************************/ private void VerifyWriteState() { if (_closed) { throw new InvalidOperationException(SR.Get(SRID.BamlWriterClosed)); } if (!_startDocumentWritten) { throw new InvalidOperationException(SR.Get(SRID.BamlWriterStartDoc)); } } /***************************************************************************\ * * BamlWriter.VerifyEndTagState * * Verify that we are in a good state to perform a record write and that * the xamlnodetype on the node type stack is of the expected type. This * is called when an end tag record is written * \***************************************************************************/ private void VerifyEndTagState( BamlRecordType expectedStartTag, BamlRecordType endTagBeingWritten) { VerifyWriteState(); BamlRecordType startTagState = Pop(); if (startTagState != expectedStartTag) { throw new InvalidOperationException(SR.Get(SRID.BamlWriterBadScope, startTagState.ToString(), endTagBeingWritten.ToString())); } } /****************************************************************************\ * * BamlWriter.GetAssembly * * Get the Assembly given a name. This uses the LoadWrapper to load the * assembly from the current directory. * NOTE: Assembly paths are not currently supported, but may be in the * future if the need arises. * \***************************************************************************/ private Assembly GetAssembly(string assemblyName) { Assembly assy = _assemblies[assemblyName] as Assembly; if (assy == null) { assy = ReflectionHelper.LoadAssembly(assemblyName, null); if (assy == null) { throw new ArgumentException(SR.Get(SRID.BamlWriterBadAssembly, assemblyName)); } else { _assemblies[assemblyName] = assy; } } return assy; } /***************************************************************************\ * * BamlWriter.GetType * * Get the Type given an assembly name where the type is declared and the * type's fully qualified name * \***************************************************************************/ private Type GetType( string assemblyName, string typeFullName) { // Get the assembly that contains the type of the element Assembly assembly = GetAssembly(assemblyName); // Now see if the type is declared in this assembly Type objectType = assembly.GetType(typeFullName); // Note that null objectType is allowed, since this may be something like // aelement in a style, which is mapped to a element tag return objectType; } /****************************************************************************\ * * BamlWriter.GetDpOrPi * * Get the DependencyProperty or the PropertyInfo that corresponds to the * passed property name on the passed owner type (or type where the * property is declared). * \***************************************************************************/ private object GetDpOrPi( Type ownerType, string propName) { // Now that we have the type, see if this is a DependencyProperty or // a PropertyInfo. Note that ownerType may be null for fake properties // like those associated with Styles. Note also that the property may // not resolve to a known DP. This is allowed for fake properties like // those contained in Styles, such as Set.Value. object dpOrPi = null; #if !PBTCOMPILER if (ownerType != null) { dpOrPi = DependencyProperty.FromName(propName, ownerType); if (dpOrPi == null) { dpOrPi = ownerType.GetProperty(propName, BindingFlags.Instance | BindingFlags.Public); } } #else if (ownerType != null) { dpOrPi = KnownTypes.Types[(int)KnownElements.DependencyProperty].InvokeMember("FromName", BindingFlags.InvokeMethod, null, null, new object[] {propName, ownerType} ); if (dpOrPi == null) { dpOrPi = ownerType.GetProperty(propName, BindingFlags.Instance | BindingFlags.Public); } } #endif return dpOrPi; } /****************************************************************************\ * * BamlWriter.GetDpOrPi * * Get the DependencyProperty or the PropertyInfo that corresponds to the * passed property name on the passed owner type name. This typename is * first resolved to a type. * \***************************************************************************/ private void GetDpOrPi( string assemblyName, string ownerTypeFullName, string propName, out object dpOrPi, out Type ownerType) { // If there is no valid owner or assembly, then we can't resolve the type, // so just return null. This can occur if we are writing 'fake' properties // that are used for things such as Style property triggers. if (assemblyName == string.Empty || ownerTypeFullName == string.Empty) { dpOrPi = null; ownerType = null; } else { ownerType = GetType(assemblyName, ownerTypeFullName); dpOrPi = GetDpOrPi(ownerType, propName); } } // Helper methods for dealing with the node stack // Push a new record type on the stack. If we have not generated an // end attributes write operation for the current item on the stack, and // that item is a start element, then now is the time to do it. private void Push(BamlRecordType recordType) { CheckEndAttributes(); _nodeTypeStack.Push(new WriteStackNode(recordType)); } private void Push(BamlRecordType recordType, Type elementType) { CheckEndAttributes(); _nodeTypeStack.Push(new WriteStackNode(recordType, elementType)); } // Pop an item off the node stack and return its type. private BamlRecordType Pop() { WriteStackNode stackNode = _nodeTypeStack.Pop() as WriteStackNode; Debug.Assert(stackNode != null); return stackNode.RecordType; } // Return the record type on the top of the stack private BamlRecordType PeekRecordType() { WriteStackNode stackNode = _nodeTypeStack.Peek() as WriteStackNode; Debug.Assert(stackNode != null); return stackNode.RecordType; } // Return the element type on the top of the stack private Type PeekElementType() { WriteStackNode stackNode = _nodeTypeStack.Peek() as WriteStackNode; Debug.Assert(stackNode != null); return stackNode.ElementType; } // Check if we have to insert an EndAttributes xaml node at the end // of an element start tag. private void CheckEndAttributes() { if (_nodeTypeStack.Count > 0) { WriteStackNode parentNode = _nodeTypeStack.Peek() as WriteStackNode; if (!parentNode.EndAttributesReached && parentNode.RecordType == BamlRecordType.ElementStart) { XamlEndAttributesNode node = new XamlEndAttributesNode( 0, 0, _depth, false); _bamlRecordWriter.WriteEndAttributes(node); } parentNode.EndAttributesReached = true; } } #endregion Internal Methods #region Data // Item pushed on a node stack to keep track for matching // end tags and for determining when the end of the // start tag has been reached for generating an end attribute write. private class WriteStackNode { public WriteStackNode( BamlRecordType recordType) { _recordType = recordType; _endAttributesReached = false; } public WriteStackNode( BamlRecordType recordType, Type elementType) : this(recordType) { _elementType = elementType; } public BamlRecordType RecordType { get { return _recordType; } } public bool EndAttributesReached { get { return _endAttributesReached; } set { _endAttributesReached = value; } } public Type ElementType { get { return _elementType; } } bool _endAttributesReached; BamlRecordType _recordType; Type _elementType; } // Writer that actually writes BamlRecords to a stream. BamlRecordWriter _bamlRecordWriter; // True if the DocumentStart record has been written to the stream. bool _startDocumentWritten; // The depth of the element tree, including complex properties int _depth; // True if Close() has been called. bool _closed; // If a custom property is of Type DependencyProperty, this is used to provide // info about the Type of value for such a property in order to write it out in // an optimized form by its custom serializer. DependencyProperty _dpProperty; // Stack of the type of nodes written to the baml stream. This is // used for end-tag matching and basic structure checking. This // contains WriteStackNode objects. ParserStack _nodeTypeStack; // Cache of assemblies that are needed for type resolutions when // doingIBamlSerialize Hashtable _assemblies; // XamlTypeMapper used by this writer XamlTypeMapper _xamlTypeMapper; // ParserContext for this writer ParserContext _parserContext; // The helper class that handles parsing of MarkupExtension values. MarkupExtensionParser _extensionParser; // Buffered XamlNodes that occur when a property with a MarkupExtension value // is written and expanded into a complex property subtree. ArrayList _markupExtensionNodes; #endregion Data } internal class BamlWriterXamlTypeMapper : XamlTypeMapper { internal BamlWriterXamlTypeMapper( string[] assemblyNames, NamespaceMapEntry[] namespaceMaps) : base(assemblyNames, namespaceMaps) { } /// /// Allows BamlWriter to allow access to legitimate internal types /// /// The internal type ////// Always Returns true by default, since this is used by localization /// sealed protected override bool AllowInternalType(Type type) { return true; } } } // File provided for Reference Use Only by Microsoft Corporation (c) 2007. // Copyright (c) Microsoft Corporation. All rights reserved.
Link Menu
This book is available now!
Buy at Amazon US or
Buy at Amazon UK
- WindowsComboBox.cs
- ExternalCalls.cs
- Binding.cs
- DecoderNLS.cs
- formatter.cs
- MessageSecurityOverHttpElement.cs
- DataGridViewTextBoxCell.cs
- SoapUnknownHeader.cs
- GreenMethods.cs
- StylusPointCollection.cs
- Msec.cs
- PageThemeCodeDomTreeGenerator.cs
- HealthMonitoringSection.cs
- TranslateTransform.cs
- XXXInfos.cs
- Speller.cs
- RadioButtonRenderer.cs
- ClientRolePrincipal.cs
- DataGridAutoFormat.cs
- XmlAnyElementAttribute.cs
- AlphabeticalEnumConverter.cs
- DisplayMemberTemplateSelector.cs
- InvalidPropValue.cs
- datacache.cs
- HtmlInputCheckBox.cs
- MDIClient.cs
- BuildDependencySet.cs
- WindowPattern.cs
- PrePostDescendentsWalker.cs
- UserControl.cs
- BulletedListEventArgs.cs
- SchemaEntity.cs
- TemplateField.cs
- Brush.cs
- TypeUtils.cs
- CatalogPartChrome.cs
- PingReply.cs
- DependencyPropertyChangedEventArgs.cs
- ObjectDataSourceMethodEventArgs.cs
- WebPartCloseVerb.cs
- TextEmbeddedObject.cs
- AsyncCompletedEventArgs.cs
- DynamicMethod.cs
- SwitchLevelAttribute.cs
- MachineSettingsSection.cs
- FreeFormDragDropManager.cs
- SharedConnectionListener.cs
- IIS7UserPrincipal.cs
- HierarchicalDataTemplate.cs
- MenuCommand.cs
- TextRunCache.cs
- EntityException.cs
- sortedlist.cs
- ProtocolsSection.cs
- VisualTreeHelper.cs
- SystemMulticastIPAddressInformation.cs
- TypeExtension.cs
- HashHelper.cs
- UInt64Converter.cs
- Stopwatch.cs
- ComponentConverter.cs
- HelloMessageCD1.cs
- ChineseLunisolarCalendar.cs
- DataServiceRequest.cs
- DataServiceHost.cs
- Boolean.cs
- IIS7UserPrincipal.cs
- TabletDevice.cs
- Transform3D.cs
- CodeAccessSecurityEngine.cs
- TemplateBindingExtension.cs
- NodeInfo.cs
- ClientBuildManagerTypeDescriptionProviderBridge.cs
- FactoryGenerator.cs
- Encoding.cs
- BitmapCache.cs
- HandleCollector.cs
- InspectionWorker.cs
- ExtractCollection.cs
- ModelPerspective.cs
- BaseContextMenu.cs
- CustomTypeDescriptor.cs
- InvalidStoreProtectionKeyException.cs
- EncodedStreamFactory.cs
- GACMembershipCondition.cs
- HTTPNotFoundHandler.cs
- Ray3DHitTestResult.cs
- XmlObjectSerializerWriteContext.cs
- PeerApplication.cs
- EncodingTable.cs
- CodeVariableDeclarationStatement.cs
- SelectionEditingBehavior.cs
- __Filters.cs
- PlatformCulture.cs
- DataViewSetting.cs
- BuildProviderCollection.cs
- XsltLoader.cs
- SequentialOutput.cs
- UpDownBaseDesigner.cs
- KeyInterop.cs