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 / MarkupExtensionParser.cs / 1 / 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: peterost - 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(
string id,
string parameter1,
int lineNumber,
int linePosition)
{
string message = SR.Get(id, parameter1);
ThrowExceptionWithLine(message, lineNumber, linePosition);
}
///
/// Throw a XamlParseException
///
void ThrowException(
string 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(
string 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.
/****************************************************************************\
*
* File: MarkupExtensionParser.cs
*
* Purpose: Parser for compact MarkupExtension syntax. Produces a series
* of xaml nodes that represent the markup.
*
* History:
* 11/09/04: peterost - 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(
string id,
string parameter1,
int lineNumber,
int linePosition)
{
string message = SR.Get(id, parameter1);
ThrowExceptionWithLine(message, lineNumber, linePosition);
}
///
/// Throw a XamlParseException
///
void ThrowException(
string 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(
string 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
- SourceItem.cs
- ActivityTypeDesigner.xaml.cs
- SourceSwitch.cs
- PreservationFileWriter.cs
- BmpBitmapEncoder.cs
- WorkflowInspectionServices.cs
- HttpDebugHandler.cs
- SchemaExporter.cs
- AsyncWaitHandle.cs
- QueryAccessibilityHelpEvent.cs
- ParameterSubsegment.cs
- ExtensibleClassFactory.cs
- OdbcError.cs
- EnumBuilder.cs
- FunctionUpdateCommand.cs
- DotExpr.cs
- CodeCatchClause.cs
- nulltextnavigator.cs
- StateMachineAction.cs
- DataViewSettingCollection.cs
- Parameter.cs
- HttpGetProtocolReflector.cs
- DbDataAdapter.cs
- ScrollableControl.cs
- BindingMemberInfo.cs
- Mouse.cs
- SQLInt32Storage.cs
- RadioButtonPopupAdapter.cs
- MessageQueuePermissionAttribute.cs
- MethodCallTranslator.cs
- MenuBindingsEditorForm.cs
- RectAnimationUsingKeyFrames.cs
- XmlWrappingWriter.cs
- DataGridBoolColumn.cs
- LinqDataSourceContextEventArgs.cs
- DefaultBinder.cs
- ClientSession.cs
- PathSegment.cs
- figurelengthconverter.cs
- ActivityTypeCodeDomSerializer.cs
- SerializationFieldInfo.cs
- SimpleHandlerBuildProvider.cs
- ChannelServices.cs
- NonVisualControlAttribute.cs
- XmlNotation.cs
- PtsPage.cs
- FacetValueContainer.cs
- FieldDescriptor.cs
- InternalDuplexChannelFactory.cs
- WinEventHandler.cs
- DSASignatureDeformatter.cs
- ResourceDefaultValueAttribute.cs
- JoinTreeSlot.cs
- PersonalizableTypeEntry.cs
- DoubleLink.cs
- SystemException.cs
- configsystem.cs
- TemplateParser.cs
- PropertyToken.cs
- HandlerWithFactory.cs
- ToolStripContentPanelRenderEventArgs.cs
- LaxModeSecurityHeaderElementInferenceEngine.cs
- DataKeyArray.cs
- DocumentCollection.cs
- SecurityPermission.cs
- NameTable.cs
- WebPartMenuStyle.cs
- EntityDataSourceEntityTypeFilterConverter.cs
- Semaphore.cs
- UIPermission.cs
- OracleParameterCollection.cs
- localization.cs
- WebPartAuthorizationEventArgs.cs
- TextTrailingWordEllipsis.cs
- CustomExpressionEventArgs.cs
- _CacheStreams.cs
- ObjectDisposedException.cs
- PersonalizablePropertyEntry.cs
- RoleService.cs
- HierarchicalDataTemplate.cs
- TransformedBitmap.cs
- ToolStripItemCollection.cs
- ListDataBindEventArgs.cs
- RequestQueryProcessor.cs
- ColorBlend.cs
- ReferencedCollectionType.cs
- COM2PictureConverter.cs
- Models.cs
- UseLicense.cs
- SmiContext.cs
- IgnorePropertiesAttribute.cs
- SqlException.cs
- System.Data_BID.cs
- SharedPersonalizationStateInfo.cs
- __ComObject.cs
- Viewport2DVisual3D.cs
- KeyValueConfigurationElement.cs
- SingleAnimation.cs
- MemoryFailPoint.cs
- ProfileGroupSettingsCollection.cs