Code:
/ DotNET / DotNET / 8.0 / untmp / WIN_WINDOWS / lh_tools_devdiv_wpf / Windows / wcp / Framework / System / Windows / Markup / MarkupExtensionParser.cs / 2 / MarkupExtensionParser.cs
/****************************************************************************\ * * File: MarkupExtensionParser.cs * * Purpose: Parser for compact MarkupExtension syntax. Produces a series * of xaml nodes that represent the markup. * * History: * 11/09/04: [....] - Created * * Copyright (C) 2004 by Microsoft Corporation. All rights reserved. * \***************************************************************************/ using System; using System.Xml; using System.IO; using System.Text; using System.Collections; using System.Diagnostics; using System.Reflection; using System.Threading; using System.Globalization; using System.Security; using System.Security.Permissions; using MS.Utility; using System.Collections.Specialized; using System.Runtime.InteropServices; #if PBTCOMPILER namespace MS.Internal.Markup #else namespace System.Windows.Markup #endif { ////// MarkupExtension parsing helper that provides things like namespace lookups /// and type resolution services. This is implemented by classes like XamlReaderHelper /// and BamlWriter. /// internal interface IParserHelper { string LookupNamespace(string prefix); bool GetElementType( bool extensionFirst, string localName, string namespaceURI, ref string assemblyName, ref string typeFullName, ref Type baseType, ref Type serializerType); bool CanResolveLocalAssemblies(); } ////// MarkupExtension parser class. /// internal class MarkupExtensionParser { ////// Constructor. /// internal MarkupExtensionParser( IParserHelper parserHelper, ParserContext parserContext) { _parserHelper = parserHelper; _parserContext = parserContext; } ////// Return an intialized AttributeData structure if the attribute value adheres to the /// format for MarkupExtensions. Otherwise return null. /// internal AttributeData IsMarkupExtensionAttribute( Type declaringType, // Type where propIdName is declared string propIdName, // Name of the property ref string attrValue, int lineNumber, int linePosition, int depth, object info) // PropertyInfo or DependencyProperty or MethodInfo for the property { string typeName; string args; if (!GetMarkupExtensionTypeAndArgs(ref attrValue, out typeName, out args)) { return null; } return FillAttributeData(declaringType, propIdName, typeName, args, attrValue, lineNumber, linePosition, depth, info); } ////// Return an intialized DefAttributeData structure if the attribute value adheres to the /// format for MarkupExtensions. Otherwise return null. /// internal DefAttributeData IsMarkupExtensionDefAttribute( Type declaringType, ref string attrValue, int lineNumber, int linePosition, int depth) { string typeName; string args; if (!GetMarkupExtensionTypeAndArgs(ref attrValue, out typeName, out args)) { return null; } return FillDefAttributeData(declaringType, typeName, args, attrValue, lineNumber, linePosition, depth); } ////// Applies some quick checks to see if an Attribute Value is a /// Markup Extension. This is meant to be much cheaper than the full /// parse. And this can return true even if the ME has some syntax errors. /// internal static bool LooksLikeAMarkupExtension(string attrValue) { if (attrValue.Length < 2) return false; if (attrValue[0] != '{') return false; if (attrValue[1] == '}') return false; return true; } #if !PBTCOMPILER ////// Given a string that is to be treated as a literal string, check /// to see if it might be mistaken for a markup extension and escape it /// accordingly. /// ////// Prefixing the string with "{}" will tell GetMarkupExtensionTypeAndArgs() /// that the remainder of the string is to be treated literally. /// internal static string AddEscapeToLiteralString( string literalString ) { string returnString = literalString; if (!String.IsNullOrEmpty(returnString) && returnString[0] == '{') { returnString = "{}" + returnString; } return returnString; } #endif // Returns an known markup extension that can have a simple value. Also returns the property // name of the extension that can be used to set the value. This is used to strip it out of // the args to get the simple value. private KnownElements GetKnownExtensionFromType(Type extensionType, out string propName) { if (KnownTypes.Types[(int)KnownElements.TypeExtension] == extensionType) { propName = "TypeName"; return KnownElements.TypeExtension; } else if (KnownTypes.Types[(int)KnownElements.StaticExtension] == extensionType) { propName = "Member"; return KnownElements.StaticExtension; } else if (KnownTypes.Types[(int)KnownElements.TemplateBindingExtension] == extensionType) { propName = "Property"; return KnownElements.TemplateBindingExtension; } else if (KnownTypes.Types[(int)KnownElements.DynamicResourceExtension] == extensionType) { propName = "ResourceKey"; return KnownElements.DynamicResourceExtension; } else if (KnownTypes.Types[(int)KnownElements.StaticResourceExtension] == extensionType) { propName = "ResourceKey"; return KnownElements.StaticResourceExtension; } propName = string.Empty; return 0; } ////// Determine if the argument string passed in can represent a valid /// type in the format /// prefix:Classname or /// TypeName = prefix:Classname /// If so, then change the args string to contain only prefix:Classname and /// return true. Otherwise, return false. /// private bool IsSimpleTypeExtensionArgs( Type extensionType, int lineNumber, int linePosition, ref string args) { if (KnownTypes.Types[(int)KnownElements.TypeExtension] == extensionType) { return IsSimpleExtensionArgs(lineNumber, linePosition, "TypeName", ref args); } return false; } ////// Determine if the argument string passed in can represent a valid /// param for a MarkuPExtension in one of these formats: /// prefix:Classname /// TypeName = prefix:Classname (TypeExtension) /// prefix:Classname.MemberName /// Member = prefix:Classname.MemberName (StaticExtension) /// Property = prefix:Classname.MemberName (TemplateBindingExtension) /// {x:Type prefix:Classname} /// {x:Static prefix:Classname.MemberName} /// StringValue /// ResourceKey = {x:Type prefix:Classname} (DynamicResourceExtension) /// ResourceKey = {x:Static prefix:Classname.MemberName} (DynamicResourceExtension) /// ResourceKey = StringValue (DynamicResourceExtension) /// If so, then change the args string to contain only the raw value: /// prefix:Classname or /// prefix:Classname.MemberName or /// StringValue /// and return true. Otherwise, return false. /// isValueNestedExtension = true, if the args value is itself a StaticExtension or TypeExtension. /// isValueTypeExtension = true, if the args value is itself a TypeExtension. /// valueExtensions only apply to DynamicResourceExtension. /// private bool IsSimpleExtension( Type extensionType, int lineNumber, int linePosition, int depth, out short extensionTypeId, out bool isValueNestedExtension, out bool isValueTypeExtension, ref string args) { bool isSimple = false; string propName; extensionTypeId = 0; isValueNestedExtension = false; isValueTypeExtension = false; // if we support optimizing for custom extensions, this can be generalized // to use a converter\serializer Id for that extension. KnownElements knownExtensionTypeId = GetKnownExtensionFromType(extensionType, out propName); if (knownExtensionTypeId != KnownElements.UnknownElement) { isSimple = IsSimpleExtensionArgs(lineNumber, linePosition, propName, ref args); } if (isSimple) { switch (knownExtensionTypeId) { case KnownElements.DynamicResourceExtension: case KnownElements.StaticResourceExtension: if (LooksLikeAMarkupExtension(args)) { // if value may be a possible ME, see if it is a simple Type\StaticExtension. // null is passed for propIdName to indicate this. AttributeData nestedAttrData = IsMarkupExtensionAttribute(extensionType, null, ref args, lineNumber, linePosition, depth, null); isValueTypeExtension = nestedAttrData.IsTypeExtension; isSimple = isValueTypeExtension || nestedAttrData.IsStaticExtension; isValueNestedExtension = isSimple; if (isSimple) { // if nested extension value is simple, take the simple args args = nestedAttrData.Args; } else { // else restore the original args for normal processing args += "}"; } } break; } if (isSimple) { extensionTypeId = (short)knownExtensionTypeId; } } return isSimple; } private bool IsSimpleExtensionArgs(int lineNumber, int linePosition, string propName, ref string args) { // We have a MarkupExtension, so process the argument string to determine // if it is simple. Do this by tokenizing now and extracting the simple // type string. ArrayList tokens = TokenizeAttributes(args, lineNumber, linePosition); if (tokens == null) { return false; } if (tokens.Count == 1) { args = (String)tokens[0]; return true; } if (tokens.Count == 3 && (string)tokens[0] == propName) { args = (String)tokens[2]; return true; } return false; } ////// Parse the attrValue string into a typename and arguments. Return true if /// they parse successfully. /// ////// Localization API also relys on this method to filter markup extensions, as they are not /// localizable by default. /// internal static bool GetMarkupExtensionTypeAndArgs( ref string attrValue, out string typeName, out string args) { int length = attrValue.Length; typeName = string.Empty; args = string.Empty; // MarkupExtensions MUST have '{' as the first character if (length < 1 || attrValue[0] != '{') { return false; } bool gotEscape = false; StringBuilder stringBuilder = null; int i = 1; for (; i/// Fill the def attribute data structure with type and attribute string information. /// Note that this is not for general def attributes, but only for the key attribute /// used when storing items in an IDictionary. /// private DefAttributeData FillDefAttributeData( Type declaringType, // Type where attribute is declared string typename, string args, string attributeValue, int lineNumber, int linePosition, int depth) { string namespaceURI; string targetAssemblyName; string targetFullName; Type targetType; Type serializerType; bool isSimple = false; bool resolvedTag = GetExtensionType(typename, attributeValue, lineNumber, linePosition, out namespaceURI, out targetAssemblyName, out targetFullName, out targetType, out serializerType); if (resolvedTag) { isSimple = IsSimpleTypeExtensionArgs(targetType, lineNumber, linePosition, ref args); } return new DefAttributeData(targetAssemblyName, targetFullName, targetType, args, declaringType, namespaceURI, lineNumber, linePosition, depth, isSimple); } // Fill the attribute data structure with type and attribute string information private AttributeData FillAttributeData( Type declaringType, // Type where propIdName is declared string propIdName, // Name of the property string typename, string args, string attributeValue, int lineNumber, int linePosition, int depth, object info) // PropertyInfo or DependencyProperty or MethodInfo for the property { string namespaceURI; string targetAssemblyName; string targetFullName; Type targetType; Type serializerType; bool isSimple = false; short extensionId = 0; bool isValueNestedExtension = false; bool isValueTypeExtension = false; bool resolvedTag = GetExtensionType(typename, attributeValue, lineNumber, linePosition, out namespaceURI, out targetAssemblyName, out targetFullName, out targetType, out serializerType); // propIdName is an empty string only for the case when args is a ctor param of a MarkupExtension if (resolvedTag && propIdName != string.Empty) { if (propIdName == null) { // If propIdName is null, then we are looking for nested simple extensions and // we allow only Type\StaticExtension. if (KnownTypes.Types[(int)KnownElements.TypeExtension] == targetType) { isSimple = IsSimpleExtensionArgs(lineNumber, linePosition, "TypeName", ref args); isValueNestedExtension = isSimple; isValueTypeExtension = isSimple; extensionId = (short)KnownElements.TypeExtension; } else if (KnownTypes.Types[(int)KnownElements.StaticExtension] == targetType) { isSimple = IsSimpleExtensionArgs(lineNumber, linePosition, "Member", ref args); isValueNestedExtension = isSimple; extensionId = (short)KnownElements.StaticExtension; } } else { propIdName = propIdName.Trim(); isSimple = IsSimpleExtension(targetType, lineNumber, linePosition, depth, out extensionId, out isValueNestedExtension, out isValueTypeExtension, ref args); } } return new AttributeData(targetAssemblyName, targetFullName, targetType, args, declaringType, propIdName, info, serializerType, lineNumber, linePosition, depth, namespaceURI, extensionId, isValueNestedExtension, isValueTypeExtension, isSimple); } private bool GetExtensionType( string typename, string attributeValue, int lineNumber, int linePosition, out string namespaceURI, out string targetAssemblyName, out string targetFullName, out Type targetType, out Type serializerType) { targetAssemblyName = null; targetFullName = null; targetType = null; serializerType = null; // lookup the type of the target string fullname = typename; string prefix = String.Empty; int typeIndex = typename.IndexOf(':'); if (typeIndex >= 0) { prefix = typename.Substring(0, typeIndex); typename = typename.Substring(typeIndex + 1); } namespaceURI = _parserHelper.LookupNamespace(prefix); bool resolvedTag = _parserHelper.GetElementType(true, typename, namespaceURI, ref targetAssemblyName, ref targetFullName, ref targetType, ref serializerType); if (!resolvedTag) { if (_parserHelper.CanResolveLocalAssemblies()) { // if local assemblies can be resolved, but the type could not be resolved, then // we need to throw an exception ThrowException(SRID.ParserNotMarkupExtension, attributeValue, typename, namespaceURI, lineNumber, linePosition); } else { // if local assemblies cannot yet be resolved, we record the data that we will need // to write an unknown tag start, and note in the data that the type is an unknown // markup extension. targetFullName = fullname; targetType = typeof(UnknownMarkupExtension); } } else if (!KnownTypes.Types[(int)KnownElements.MarkupExtension].IsAssignableFrom(targetType)) { // if the type is not known, throw an exception ThrowException(SRID.ParserNotMarkupExtension, attributeValue, typename, namespaceURI, lineNumber, linePosition); } return resolvedTag; } /// /// Fill the attribute data structure with type and attribute string information. /// Note that this is for general properties and not def attributes. /// internal ArrayList CompileAttributes( ArrayList markupExtensionList, int startingDepth) { ArrayList xamlNodes = new ArrayList(markupExtensionList.Count * 5); for (int i = 0; i/// Create nodes for a complex property that surrounds an element tree. /// Note that this is for general properties and not def attributes. /// internal void CompileAttribute( ArrayList xamlNodes, AttributeData data) { // For MarkupExtension syntax, treat PropertyInfo as a CLR property, but MethodInfo // and DependencyProperty as a DependencyProperty. Note that the MarkupCompiler // will handle the case where a DependencyProperty callback is made if it gets // a MethodInfo for the attached property setter. string declaringTypeAssemblyName = data.DeclaringType.Assembly.FullName; string declaringTypeFullName = data.DeclaringType.FullName; // Find the PropertyRecordType to use in this case Type propertyType; bool propertyCanWrite; XamlTypeMapper.GetPropertyType(data.Info, out propertyType, out propertyCanWrite); BamlRecordType propertyRecordType = BamlRecordManager.GetPropertyStartRecordType(propertyType, propertyCanWrite); // Create the property start and end records XamlNode propertyStart; XamlNode propertyEnd; switch (propertyRecordType) { case BamlRecordType.PropertyArrayStart: { propertyStart = new XamlPropertyArrayStartNode( data.LineNumber, data.LinePosition, data.Depth, data.Info, declaringTypeAssemblyName, declaringTypeFullName, data.PropertyName); propertyEnd = new XamlPropertyArrayEndNode( data.LineNumber, data.LinePosition, data.Depth); break; } case BamlRecordType.PropertyIDictionaryStart: { propertyStart = new XamlPropertyIDictionaryStartNode( data.LineNumber, data.LinePosition, data.Depth, data.Info, declaringTypeAssemblyName, declaringTypeFullName, data.PropertyName); propertyEnd = new XamlPropertyIDictionaryEndNode( data.LineNumber, data.LinePosition, data.Depth); break; } case BamlRecordType.PropertyIListStart: { propertyStart = new XamlPropertyIListStartNode( data.LineNumber, data.LinePosition, data.Depth, data.Info, declaringTypeAssemblyName, declaringTypeFullName, data.PropertyName); propertyEnd = new XamlPropertyIListEndNode( data.LineNumber, data.LinePosition, data.Depth); break; } default: // PropertyComplexStart { propertyStart = new XamlPropertyComplexStartNode( data.LineNumber, data.LinePosition, data.Depth, data.Info, declaringTypeAssemblyName, declaringTypeFullName, data.PropertyName); propertyEnd = new XamlPropertyComplexEndNode( data.LineNumber, data.LinePosition, data.Depth); break; } } // NOTE: Add duplicate property checking here as is done in XamlReaderHelper xamlNodes.Add(propertyStart); CompileAttributeCore(xamlNodes, data); xamlNodes.Add(propertyEnd); } /// /// Create nodes for an element tree. /// Note that this is for general properties and not def attributes. /// internal void CompileAttributeCore( ArrayList xamlNodes, AttributeData data) { string typename = null; string namespaceURI = null; ArrayList list = TokenizeAttributes(data.Args, data.LineNumber, data.LinePosition); // If the list is empty, or the second item on the list is an equal sign, then // we have a simple markup extension that uses the default constructor. In all // other cases we must have at least one constructor parameter. if (data.TargetType == typeof(UnknownMarkupExtension)) { // If the target type is unknown, then we record an unknown tag start, rather // than an element start. typename = data.TargetFullName; string prefix = String.Empty; int typeIndex = typename.IndexOf(':'); if (typeIndex >= 0) { prefix = typename.Substring(0, typeIndex); typename = typename.Substring(typeIndex + 1); } namespaceURI = _parserHelper.LookupNamespace(prefix); xamlNodes.Add(new XamlUnknownTagStartNode( data.LineNumber, data.LinePosition, ++data.Depth, namespaceURI, typename)); } else { xamlNodes.Add(new XamlElementStartNode( data.LineNumber, data.LinePosition, ++data.Depth, data.TargetAssemblyName, data.TargetFullName, data.TargetType, data.SerializerType)); } xamlNodes.Add(new XamlEndAttributesNode( data.LineNumber, data.LinePosition, data.Depth, true)); int listIndex = 0; if (list != null && (list.Count == 1 || (list.Count > 1 && !(list[1] is String) && ((Char)list[1] == ',')))) { // Go through the constructor parameters, writing them out like complex // properties WriteConstructorParams(xamlNodes, list, data, ref listIndex); } // Write properties that come after the element constructor parameters WriteProperties(xamlNodes, list, listIndex, data); // close up if (data.TargetType == typeof(UnknownMarkupExtension)) { xamlNodes.Add(new XamlUnknownTagEndNode( data.LineNumber, data.LinePosition, data.Depth--, typename, namespaceURI)); } else { xamlNodes.Add(new XamlElementEndNode( data.LineNumber, data.LinePosition, data.Depth--)); } } ////// Parse the string representation of a set of def attributes in MarkupExtension /// syntax and return a list of xaml nodes that represents those attributes. /// internal ArrayList CompileDictionaryKeys( ArrayList complexDefAttributesList, int startingDepth) { ArrayList xamlNodes = new ArrayList(complexDefAttributesList.Count * 5); for (int i = 0; i/// Parse the string representation of a set of def attributes in MarkupExtension /// syntax and return a list of xaml nodes that represents those attributes. /// internal void CompileDictionaryKey( ArrayList xamlNodes, DefAttributeData data) { ArrayList list = TokenizeAttributes(data.Args, data.LineNumber, data.LinePosition); // If the list is empty, or the second item on the list is an equal sign, then // we have a simple markup extension that uses the default constructor. In all // other cases we must have at least one constructor parameter. xamlNodes.Add(new XamlKeyElementStartNode( data.LineNumber, data.LinePosition, ++data.Depth, data.TargetAssemblyName, data.TargetFullName, data.TargetType, null)); xamlNodes.Add(new XamlEndAttributesNode( data.LineNumber, data.LinePosition, data.Depth, true)); int listIndex = 0; if (list != null && (list.Count == 1 || (list.Count > 1 && !(list[1] is String) && ((Char)list[1] == ',')))) { // Go through the constructor parameters, writing them out like complex // properties WriteConstructorParams(xamlNodes, list, data, ref listIndex); } // Write properties that come after the element constructor parameters WriteProperties(xamlNodes, list, listIndex, data); // close up xamlNodes.Add(new XamlKeyElementEndNode( data.LineNumber, data.LinePosition, data.Depth--)); } /// /// The core method that writes out the MarkupExtension itself without the surrounding /// contextual xaml nodes. /// The format of compact syntax is /// "{typename constParam1, constParam2, name = value, name = value, ... }" /// (whitespace is ignored near delimiters). The effect of this is to /// create a new object of the given type, using the provided constructor /// parameters. If they are absent, use the default constructor. Then to set /// its properties. Each name=value pair causes a property /// with the given name to be set to the given value (after type conversion). /// /// For constructor parameters, or on right-hand side of the = sign, some /// characters are treated specially: /// \ - escape charater - quotes the following character (including \) /// , - terminates the clause /// {} - matching braces, meaning a nested MarkupExtension /// '' - matching single quotes /// "" - matching double quotes /// Inside matching delimiters, comma has no special meaning and /// delimiters must nest correctly (unless escaped). Inside matching /// quotes (either kind), no characters are special except \. /// /// If the string really is in "MarkupExtension syntax" form, return true. /// /// Exceptions are thrown for mismatching delimiters, and for errors while /// assigning to properties. /// private ArrayList TokenizeAttributes ( string args, int lineNumber, int linePosition) { ArrayList list = null; int length = args.Length; bool inQuotes = false; bool gotEscape = false; bool nonWhitespaceFound = false; bool gotFinalCloseCurly = false; Char quoteChar = '\''; int leftCurlies = 0; StringBuilder stringBuilder = null; int i = 0; // Loop through the args, creating a list of arguments and known delimiters. // This loop does limited syntax checking, and serves to tokenize the argument // string into chunks that are validated in greater detail in the next phase. for (i=0; i < length && !gotFinalCloseCurly; i++) { // Escape character is always in effect for everything inside // a MarkupExtension. We have to remember that the next character is // escaped, and is not treated as a quote or delimiter. if (!gotEscape && args[i] == '\\') { gotEscape = true; continue; } if (!nonWhitespaceFound && !Char.IsWhiteSpace(args[i])) { nonWhitespaceFound = true; } // Process all characters that are not whitespace or are between quotes if (inQuotes || leftCurlies > 0 || nonWhitespaceFound) { // We have a non-whitespace character, so ensure we have // a string builder to accumulate characters and a list to collect // attributes and delimiters. These are lazily // created so that simple cases that have no parameters do not // create any extra objects. if (stringBuilder == null) { stringBuilder = new StringBuilder(length); list = new ArrayList(1); } // If the character is escaped, then it is part of the attribute // being collected, regardless of its value and is not treated as // a delimiter or special character. Write back the escape // character since downstream processing will need it to determine // whether the value is a MarkupExtension or not, and to prevent // multiple escapes from being lost by recursive processing. if (gotEscape) { stringBuilder.Append('\\'); stringBuilder.Append(args[i]); gotEscape = false; continue; } // If quoted or inside curlies then scoop up everything. // Track yet deeper nestings of curlies. if (inQuotes || leftCurlies > 0) { if (inQuotes && args[i] == quoteChar) { // If we're inside quotes, then only an end quote that is not // escaped is special, and will act as a delimiter. inQuotes = false; nonWhitespaceFound = false; // Don't trim leading and trailing spaces that were inside quotes. AddToTokenList(list, stringBuilder, false); } else { if (leftCurlies > 0 && args[i] == '}') { leftCurlies--; } else if (args[i] == '{') { leftCurlies++; } stringBuilder.Append(args[i]); } } else // not in quotes or inside nested curlies. Parse the Tokens { switch(args[i]) { case '"': case '\'': // If we're not inside quotes, then a start quote can only // occur as the first non-whitespace character in a name or value. if (stringBuilder.Length != 0) { ThrowException(SRID.ParserMarkupExtensionNoQuotesInName, args, lineNumber, linePosition); } inQuotes = true; quoteChar = args[i]; break; case ',': case '=': // If we have a token in the stringbuilder, then store it if (stringBuilder != null && stringBuilder.Length > 0) { AddToTokenList(list, stringBuilder, true); } else if (list.Count == 0) { // Must have an attribute before you have the first delimeter ThrowException(SRID.ParserMarkupExtensionDelimiterBeforeFirstAttribute, args, lineNumber, linePosition); } else if (list[list.Count - 1] is Char) { // Can't have two delimiters in a row, so check what is on // the list and complain if the last item is a character, or if // a delimiter is the first item. ThrowException(SRID.ParserMarkupExtensionBadDelimiter, args, lineNumber, linePosition); } // Append known delimiters. list.Add(args[i]); nonWhitespaceFound = false; break; case '}': // If we hit the outside right curly brace, then end processing. If // there is a delimiter on the top of the stack and we haven't // hit another non-whitespace character, then its an error gotFinalCloseCurly = true; if (stringBuilder != null) { if (stringBuilder.Length > 0) { AddToTokenList(list, stringBuilder, true); } else if (list.Count > 0 && (list[list.Count-1] is Char)) { ThrowException(SRID.ParserMarkupExtensionBadDelimiter, args, lineNumber, linePosition); } } break; case '{': leftCurlies++; stringBuilder.Append(args[i]); break; default: // Must just be a plain old character, so add it to the stringbuilder stringBuilder.Append(args[i]); break; } } } } // If we've accumulated content but haven't hit a terminating '}' then the // format is bad, so complain. if (!gotFinalCloseCurly) { ThrowException(SRID.ParserMarkupExtensionNoEndCurlie, "}", lineNumber, linePosition); } // If there is junk after the closing '}', complain else if (i < length) { ThrowException(SRID.ParserMarkupExtensionTrailingGarbage, "}", args.Substring(i,length-(i)), lineNumber, linePosition); } return list; } private static void AddToTokenList(ArrayList list, StringBuilder sb, bool trim) { if(trim) { Debug.Assert(sb.Length > 0); Debug.Assert(!Char.IsWhiteSpace(sb[0])); int i = sb.Length-1; while(Char.IsWhiteSpace(sb[i])) --i; sb.Length = i+1; } list.Add(sb.ToString()); sb.Length = 0; } ////// At this point the start element is written, and we have to process all /// the constructor parameters that follow. Stop when we hit the first /// name=value, or when the end of the attributes is reached. /// private void WriteConstructorParams( ArrayList xamlNodes, ArrayList list, DefAttributeData data, ref int listIndex) { #if PBTCOMPILER int numberOfConstructorAttributes = 0; #endif if (list != null && listIndex < list.Count) { // Mark the start of the constructor parameter section. Nodes directly // under this one are constructor parameters. Note that we can have // element trees under here. xamlNodes.Add(new XamlConstructorParametersStartNode( data.LineNumber, data.LinePosition, ++data.Depth)); for (; listIndex < list.Count; listIndex+=2) { if (!(list[listIndex] is String)) { ThrowException(SRID.ParserMarkupExtensionBadConstructorParam, data.Args, data.LineNumber, data.LinePosition); } // If the next item after the current one is '=', then we've hit the // start of named parameters, so stop if (list.Count > (listIndex+1) && list[listIndex+1] is Char && (Char)list[listIndex+1] == '=') { break; } #if PBTCOMPILER numberOfConstructorAttributes++; #endif // Handle nested markup extensions by recursing here. If the // value is not a markup extension, just store it as text for // runtime resolution as a constructor parameter. string value = (String)list[listIndex]; AttributeData nestedData = IsMarkupExtensionAttribute(data.DeclaringType, string.Empty, ref value, data.LineNumber, data.LinePosition, data.Depth, null); if (nestedData == null) { RemoveEscapes(ref value); xamlNodes.Add(new XamlTextNode( data.LineNumber, data.LinePosition, data.Depth, value, null)); } else { CompileAttributeCore(xamlNodes, nestedData); } } // End of constructor parameter section. xamlNodes.Add(new XamlConstructorParametersEndNode( data.LineNumber, data.LinePosition, data.Depth--)); #if PBTCOMPILER if (data.TargetType != typeof(UnknownMarkupExtension)) { // For compile mode, check that there is a constructor with the correct // number of arguments. In xaml load scenarios, the BamlRecordReader // will do this, so don't bother doing it here. If the target type is // unknown, then we defer this check until it can be resolved. ConstructorInfo[] infos = data.TargetType.GetConstructors(BindingFlags.Public | BindingFlags.Instance); for (int i=0; i/// At this point the start element is created and all the constructor params /// have been processed. Now handle the name=value pairs as property sets. /// If we have used a regular start element record, then just use regular /// properties. /// private void WriteProperties( ArrayList xamlNodes, ArrayList list, int listIndex, DefAttributeData data) { if (list != null && listIndex < list.Count) { ArrayList propertyNamesSoFar = new ArrayList(list.Count/4); for (int k = listIndex; k < list.Count; k+=4) { // if (k > (list.Count-3) || (list[k+1] is String) || ((Char)list[k+1]) != '=') { ThrowException(SRID.ParserMarkupExtensionNoNameValue, data.Args, data.LineNumber, data.LinePosition); } // See if this is a duplicate property definition, and throw if it is. string propertyName = list[k] as String; propertyName = propertyName.Trim(); if (propertyNamesSoFar.Contains(propertyName)) { ThrowException(SRID.ParserDuplicateMarkupExtensionProperty, propertyName, data.LineNumber, data.LinePosition); } propertyNamesSoFar.Add( propertyName ); // Fetch the property context int nameIndex = propertyName.IndexOf(':'); string localName = (nameIndex < 0) ? propertyName : propertyName.Substring(nameIndex+1); string prefix = (nameIndex < 0) ? String.Empty : propertyName.Substring(0, nameIndex); string attribNamespaceURI = ResolveAttributeNamespaceURI(prefix, localName, data.TargetNamespaceUri); object dynamicObject; string assemblyName; string typeFullName; Type declaringType; string dynamicObjectName; AttributeContext attributeContext = GetAttributeContext( data.TargetType, data.TargetNamespaceUri, attribNamespaceURI, localName, out dynamicObject, out assemblyName, out typeFullName, out declaringType, out dynamicObjectName); // Handle nested markup extensions by recursing here. If the // value is not a markup extension, just store it as text for // runtime resolution. string strValue = list[k+2] as String; AttributeData nestedAttrData = IsMarkupExtensionAttribute( data.TargetType, propertyName, ref strValue, data.LineNumber, data.LinePosition, data.Depth, dynamicObject); list[k+2] = strValue; if (nestedAttrData != null) { if (nestedAttrData.IsSimple) { CompileProperty(xamlNodes, propertyName, nestedAttrData.Args, data.TargetType, data.TargetNamespaceUri, // xmlns of TargetType nestedAttrData, nestedAttrData.LineNumber, nestedAttrData.LinePosition, nestedAttrData.Depth); } else { // Bug: Need to check validity of property by calling GetAttributeContext here? CompileAttribute(xamlNodes, nestedAttrData); } } else if (!data.IsUnknownExtension) { CompileProperty(xamlNodes, propertyName, ((String)list[k+2]), data.TargetType, data.TargetNamespaceUri, // xmlns of TargetType null, data.LineNumber, data.LinePosition, data.Depth); } } } } private string ResolveAttributeNamespaceURI(string prefix, string name, string parentURI) { string attribNamespaceURI; if(!String.IsNullOrEmpty(prefix)) { attribNamespaceURI = _parserHelper.LookupNamespace(prefix); } else { // if the prefix was "" then // 1) normal properties resolve to the parent Tag namespace. // 2) Attached properties resolve to the "" default namespace. int dotIndex = name.IndexOf('.'); if (-1 == dotIndex) attribNamespaceURI = parentURI; else attribNamespaceURI = _parserHelper.LookupNamespace(""); } return attribNamespaceURI; } /// /// Represent a single property for a MarkupExtension as a complex property. /// private void CompileProperty( ArrayList xamlNodes, string name, string value, Type parentType, string parentTypeNamespaceUri, AttributeData data, int lineNumber, int linePosition, int depth) { RemoveEscapes(ref name); RemoveEscapes(ref value); int nameIndex = name.IndexOf(':'); string localName = (nameIndex < 0) ? name : name.Substring(nameIndex+1); string prefix = (nameIndex < 0) ? String.Empty : name.Substring(0, nameIndex); string attribNamespaceURI = ResolveAttributeNamespaceURI(prefix, localName, parentTypeNamespaceUri); object dynamicObject; string assemblyName; string typeFullName; Type declaringType; string dynamicObjectName; if (String.IsNullOrEmpty(attribNamespaceURI)) { ThrowException(SRID.ParserPrefixNSProperty, prefix, name, lineNumber, linePosition); } AttributeContext attributeContext = GetAttributeContext( parentType, parentTypeNamespaceUri, attribNamespaceURI, localName, out dynamicObject, out assemblyName, out typeFullName, out declaringType, out dynamicObjectName); if (attributeContext != AttributeContext.Property) { ThrowException(SRID.ParserMarkupExtensionUnknownAttr, localName, parentType.FullName, lineNumber, linePosition); } MemberInfo info = dynamicObject as MemberInfo; Debug.Assert(null != info, "No property or method info for field Name"); if (data != null && data.IsSimple) { if (data.IsTypeExtension) { string typeValueFullName = value; // set this to original value for error reporting if reqd. string typeValueAssemblyFullName = null; Type typeValue = _parserContext.XamlTypeMapper.GetTypeFromBaseString(value, _parserContext, true); if (typeValue != null) { typeValueFullName = typeValue.FullName; typeValueAssemblyFullName = typeValue.Assembly.FullName; } XamlPropertyWithTypeNode xamlPropertyWithTypeNode = new XamlPropertyWithTypeNode(data.LineNumber, data.LinePosition, data.Depth, dynamicObject, assemblyName, typeFullName, localName, typeValueFullName, typeValueAssemblyFullName, typeValue, string.Empty, string.Empty); xamlNodes.Add(xamlPropertyWithTypeNode); } else { XamlPropertyWithExtensionNode xamlPropertyWithExtensionNode = new XamlPropertyWithExtensionNode(data.LineNumber, data.LinePosition, data.Depth, dynamicObject, assemblyName, typeFullName, localName, value, data.ExtensionTypeId, data.IsValueNestedExtension, data.IsValueTypeExtension); xamlNodes.Add(xamlPropertyWithExtensionNode); } } else { XamlPropertyNode xamlPropertyNode = new XamlPropertyNode(lineNumber, linePosition, depth, dynamicObject, assemblyName, typeFullName, dynamicObjectName, value, BamlAttributeUsage.Default, true); xamlNodes.Add(xamlPropertyNode); } } ////// Remove any '\' escape characters from the passed string. This does a simple /// pass through the string and won't do anything if there are no '\' characters. /// internal static void RemoveEscapes(ref string value) { StringBuilder builder=null; bool noEscape = true; for (int i = 0; i < value.Length; i++) { if (noEscape && value[i] == '\\') { if (builder == null) { builder = new StringBuilder(value.Length); builder.Append(value.Substring(0,i)); } noEscape = false; } else if (builder != null) { builder.Append(value[i]); noEscape = true; } } if (builder != null) { value = builder.ToString(); } } ////// Get property information for an attribute in a MarkupExtension. This is /// very similar code to what is done in XamlReaderHelper, but we only look for clr /// properties here, since MarkupExtensions don't support events or /// DependencyProperties. /// AttributeContext GetAttributeContext( Type elementBaseType, string elementBaseTypeNamespaceUri, string attributeNamespaceUri, string attributeLocalName, out Object dynamicObject, // resolved object. out string assemblyName, // assemblyName the declaringType is found in out string typeFullName, // typeFullName of the object that the field is on out Type declaringType, // type of the object that the field is on out string dynamicObjectName) // name of the dynamicObject if found one { AttributeContext attributeContext = AttributeContext.Unknown; dynamicObject = null; assemblyName = null; typeFullName = null; declaringType = null; dynamicObjectName = null; // First, check if this is a CLR property using Static setter name // matching or property info lookups on element base type. MemberInfo mi = _parserContext.XamlTypeMapper.GetClrInfo(false, elementBaseType, attributeNamespaceUri, attributeLocalName, ref dynamicObjectName); if (null != mi) { attributeContext = AttributeContext.Property; dynamicObject = mi; declaringType = mi.DeclaringType; typeFullName = declaringType.FullName; assemblyName = declaringType.Assembly.FullName; } return attributeContext; } ////// Throw a XamlParseException /// void ThrowException( SRID id, string parameter1, int lineNumber, int linePosition) { string message = SR.Get(id, parameter1); ThrowExceptionWithLine(message, lineNumber, linePosition); } ////// Throw a XamlParseException /// void ThrowException( SRID id, string parameter1, string parameter2, int lineNumber, int linePosition) { string message = SR.Get(id, parameter1, parameter2); ThrowExceptionWithLine(message, lineNumber, linePosition); } ////// Throw a XamlParseException /// void ThrowException( SRID id, string parameter1, string parameter2, string parameter3, int lineNumber, int linePosition) { string message = SR.Get(id, parameter1, parameter2, parameter3); ThrowExceptionWithLine(message, lineNumber, linePosition); } ////// Throw a XamlParseException /// void ThrowExceptionWithLine( string message, int lineNumber, int linePosition) { message += " "; message += SR.Get(SRID.ParserLineAndOffset, lineNumber.ToString(CultureInfo.CurrentCulture), linePosition.ToString(CultureInfo.CurrentCulture)); XamlParseException parseException = new XamlParseException(message, lineNumber, linePosition); throw parseException; } // Helper that provices namespace and type resolutions private IParserHelper _parserHelper; // Parser Context for the current node being parsed. private ParserContext _parserContext; internal class UnknownMarkupExtension { } } } // 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
- SingleAnimationUsingKeyFrames.cs
- PeekCompletedEventArgs.cs
- JumpPath.cs
- CompressionTransform.cs
- TagMapInfo.cs
- LinqDataSourceDisposeEventArgs.cs
- GlobalizationAssembly.cs
- sqlnorm.cs
- SvcMapFile.cs
- ControllableStoryboardAction.cs
- WebServiceHandlerFactory.cs
- WebPartUserCapability.cs
- DataGridViewColumnDesignTimeVisibleAttribute.cs
- Double.cs
- ConfigurationValues.cs
- WebServiceEnumData.cs
- CodeLinePragma.cs
- ComplexBindingPropertiesAttribute.cs
- IItemContainerGenerator.cs
- CompilationUtil.cs
- FixedElement.cs
- ColorBlend.cs
- XmlRootAttribute.cs
- OdbcUtils.cs
- URL.cs
- TreeViewEvent.cs
- XmlSchemaExporter.cs
- ObjectAssociationEndMapping.cs
- PersonalizationProvider.cs
- StringAnimationUsingKeyFrames.cs
- WindowsListViewGroupHelper.cs
- ZeroOpNode.cs
- ReachVisualSerializer.cs
- DictionaryContent.cs
- UIElement.cs
- Enum.cs
- MessageDecoder.cs
- HttpCacheParams.cs
- Signature.cs
- DrawItemEvent.cs
- FileSystemWatcher.cs
- MessageQueueException.cs
- ProgressChangedEventArgs.cs
- WebMessageBodyStyleHelper.cs
- ButtonRenderer.cs
- SourceLocationProvider.cs
- AspNetHostingPermission.cs
- MdImport.cs
- CollectionViewGroup.cs
- Vector3DCollection.cs
- XmlToDatasetMap.cs
- ExplicitDiscriminatorMap.cs
- TextTreeRootNode.cs
- HttpCacheVary.cs
- cookie.cs
- ToolStripOverflow.cs
- ProviderIncompatibleException.cs
- TreeViewDesigner.cs
- StringBuilder.cs
- CompilationUtil.cs
- PolicyUnit.cs
- _Connection.cs
- TraceProvider.cs
- Double.cs
- ReadOnlyDataSourceView.cs
- HostingEnvironmentWrapper.cs
- ZipIOCentralDirectoryFileHeader.cs
- ArgumentOutOfRangeException.cs
- SignalGate.cs
- LinqDataSourceHelper.cs
- EditorZoneDesigner.cs
- DetailsViewUpdatedEventArgs.cs
- EndCreateSecurityTokenRequest.cs
- ReferencedType.cs
- WSSecurityPolicy12.cs
- IntegerValidatorAttribute.cs
- EventRoute.cs
- ValidationEventArgs.cs
- InputLanguageEventArgs.cs
- WindowPatternIdentifiers.cs
- TextEditorDragDrop.cs
- Constants.cs
- TimelineCollection.cs
- ThemeDirectoryCompiler.cs
- OutputCacheModule.cs
- DataFormat.cs
- ScriptResourceAttribute.cs
- GregorianCalendar.cs
- JavaScriptSerializer.cs
- ReadOnlyHierarchicalDataSource.cs
- PingOptions.cs
- MediaContext.cs
- DoubleLinkList.cs
- SortExpressionBuilder.cs
- BufferedReadStream.cs
- Fx.cs
- LabelAutomationPeer.cs
- AutomationPeer.cs
- EdgeProfileValidation.cs
- PointCollection.cs