private void CompileContentProperty(ElementContextStackData context) { int depth = XmlReader.Depth; object contentPropertyInfo = context.ContentPropertyInfo; Type propertyDeclaringType = XamlTypeMapper.GetDeclaringType(contentPropertyInfo); string propertyAssemblyName = propertyDeclaringType.Assembly.FullName; string contentPropertyName = context.ContentPropertyName; WriteContentProperty(depth, LineNumber, LinePosition, contentPropertyInfo, propertyAssemblyName, propertyDeclaringType.FullName, contentPropertyName); }
private void ResolveContentProperty(string contentPropertyName, Type elementType, string namespaceUri, out string propertyAssemblyName, out object propertyDynamicObject, out Type propertyDeclaringType) { propertyDynamicObject = null; propertyAssemblyName = null; propertyDeclaringType = null; string propertyTypeFullName = null; string propertyDynamicObjectName = null; Type propertyBaseType = null; // Push a frame for GetPropertyComplex() ElementContextStackData elementContextStackData = new ElementContextStackData(); elementContextStackData.ContextType = ElementContextType.Default; ElementContextStack.Push(elementContextStackData); bool resolved = GetPropertyComplex(elementType.Name, contentPropertyName, namespaceUri, ref propertyAssemblyName, ref propertyTypeFullName, ref propertyDynamicObjectName, ref propertyBaseType, ref propertyDynamicObject, ref propertyDeclaringType); ElementContextStack.Pop(); PropertyInfo pi = propertyDynamicObject as PropertyInfo; if (!resolved /* resolution was unsuccessful */ || pi == null /* resolved object is not a PropertyInfo */) { ThrowException(SRID.ParserInvalidContentPropertyAttribute, elementType.FullName, contentPropertyName); } // check to see if content property is accessible\allowed. bool allowed = true; if (typeof(IList).IsAssignableFrom(pi.PropertyType)) { allowed = XamlTypeMapper.IsAllowedPropertyGet(pi); } else if (!IsAssignableToIXmlSerializable(pi.PropertyType)) { allowed = XamlTypeMapper.IsAllowedPropertySet(pi); } // We will resolve the content Property even for IAddChild using types // because we need to match IAddChild Content against Content Property "content" // at "check duplicate property usage" time. // This is made more difficult because of the IAddChild using types (FixedDocument.Pages // DocumentSequence.References) are non-IList collections and thus fail the above // allowed check. But it is OK if we are IAddChild because we won't be using the // content property anyway. if (!allowed && !BamlRecordManager.TreatAsIAddChild(elementType)) { ThrowException(SRID.ParserCantSetContentProperty, contentPropertyName, elementType.Name); } }
// ContentPropertySeesAProperty drives the "Contiguous Content" state machine. // on property elements move from state During to state After. private void ContentPropertySeesAProperty(ElementContextStackData context) { if (context.ContentParserState == ParsingContent.During) { context.ContentParserState = ParsingContent.After; } }
// VerifyContentPropertySeesAnElement drives the "Contiguous Content" state machine. // All Content must be together with no property elements interruping the // list of content elements. // This method will throw on interrupted groups of content. But in the // case of a Content Property that is not a collection it will return // false to the caller; who is in a position to generate a better error. private bool VerifyContentPropertySeesAnElement(ElementContextStackData context) { // If this is the first element then all is good. if (context.ContentParserState == ParsingContent.Before) { context.ContentParserState = ParsingContent.During; return true; } // if this is the 2nd or later elements the Property must be a container else if (context.ContentParserState == ParsingContent.During) { return context.IsContentPropertyACollection; } // if we are done added content then adding content is now an error. else // if (context.ContentParserState == ParsingContent.During) { ThrowException(SRID.ParserContentMustBeContiguous); return false; // compiler demands this. } }
//Used to determine if a container can hold more than one child. private static bool IsACollection(ElementContextStackData context) { //If there is a CPA, check if the property's type is a collection if (context.IsContentPropertySet) { Type contentPropertyType = XamlTypeMapper.GetPropertyType(context.ContentPropertyInfo); return IsACollection(contentPropertyType); } else //otherwise, look at the contexttype and decide with that data. { ElementContextType contextType = context.ContextType; switch (contextType) { //These can all hold multiple items case ElementContextType.PropertyIList: case ElementContextType.PropertyIDictionary: case ElementContextType.PropertyArray: //In markup compile pass 1, we need to pretend that it is a collection...since it may be. //If it is not a collection, when pass 2 happens, the right answer will be provided. case ElementContextType.Unknown: return true; //Otherwise, look at the contextData's type to see if it is a collection or supports IAddChildInternal case ElementContextType.Default: case ElementContextType.PropertyComplex: Type t = ((Type)context.ContextData); if (IsACollection(t) || BamlRecordManager.TreatAsIAddChild(t)) return true; break; } return false; } }
/// <summary> /// Called by ReadXAML when an Element node is encountered /// </summary> /// <returns>true if parsing should continue</returns> bool ReadElementNode() { // element can be of the following types // - Standard DependencyObjects to put in the Tree // - <x: tags or other tags used by the Compiler // - .net objects // Get the IsEmptyElement value before getting attributes because // attribute loop will reset the value. bool isEmptyElement = XmlReader.IsEmptyElement; bool endTagHasBeenRead = false; // Put an item on the context stack for this element. The ContextType // may be modified by the call to CompileBamlTag. ElementContextStackData elementContextStackData = new ElementContextStackData(); elementContextStackData.IsEmptyElement = isEmptyElement; // If we have a parent stack, this context is the same as the parent // by default. if (null == CurrentContext) { elementContextStackData.ContextType = ElementContextType.Default; } else { if(ShouldImplyContentProperty()) { ElementContextStackData CpaStackData = new ElementContextStackData(); CpaStackData.ContextType = ElementContextType.Default; ElementContextStack.Push(CpaStackData); ParserContext.PushScope(); CompileContentProperty(ParentContext); ElementContextStack.Pop(); ParserContext.PopScope(); } elementContextStackData.ContextType = CurrentContext.ContextType; } ElementContextStack.Push(elementContextStackData); ParserContext.PushScope(); // Compile this tag. // Handler should return with the TextReader position either at the // current Node if plan on reading children or at the End of the Current Node. CompileBamlTag(XmlNodeType.Element, ref endTagHasBeenRead); // if the Element is empty or the caller read past the endTag then // call EndElement so Start and ends are balanced. if (isEmptyElement) { //Review - should only read past the EndTag for literal or x: // if read pastEnd tag for something that turns into an element // the proper EndElementRecord won't be generated so we should either // add the logic to make sure this doesn't happen or not guarantee // the endElement. if (!endTagHasBeenRead) { CompileBamlTag(XmlNodeType.EndElement, ref endTagHasBeenRead); Debug.Assert(false == endTagHasBeenRead, "Read past end tag on end tag"); } // Empty Complex Properties of type Dictionary should be popped now as // a start and end element tag will be added making it really non empty. // Ideally the parser should recognize this case and not add any implicit // start\end elements in this case. If\when that is fixed, this code below // can be removed. if (CurrentContext != null && CurrentContext.ContextType == ElementContextType.PropertyIDictionary) { ElementContextStack.Pop(); ParserContext.PopScope(); } } else if (endTagHasBeenRead) { // Note that we should not pop the stack here since that context info // might be needed when the empty element's attributes are being processed. ElementContextStack.Pop(); ParserContext.PopScope(); } return true; }