/// <summary> /// 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. /// </summary> 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); } } } }
/// <summary> /// Parse the string representation of a set of def attributes in MarkupExtension /// syntax and return a list of xaml nodes that represents those attributes. /// </summary> 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--)); }
/// <summary> /// 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. /// </summary> 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<infos.Length; i++) { ConstructorInfo info = infos[i]; ParameterInfo[] paramInfos = info.GetParameters(); if (paramInfos.Length == numberOfConstructorAttributes) { // Found a constructor with the right number of arguments return; } } // If we get to here, then no matching constructor was found, so complain ThrowException(SRID.ParserBadConstructorParams, data.TargetType.Name, numberOfConstructorAttributes.ToString(CultureInfo.CurrentCulture), data.LineNumber, data.LinePosition); } #endif } }