private BamlKeyInfo CheckForSharedness() { IBamlDictionaryKey dictKey = (IBamlDictionaryKey)_currentBamlRecord; Debug.Assert(dictKey != null, "Bad Key record"); if (!dictKey.SharedSet) return null; BamlKeyInfo info = new BamlKeyInfo(); info.Value = dictKey.Shared.ToString(); info.AssemblyName = string.Empty; info.Prefix = (string)_prefixDictionary[XamlReaderHelper.DefinitionNamespaceURI]; info.XmlNamespace = XamlReaderHelper.DefinitionNamespaceURI; info.ClrNamespace = string.Empty; info.Name = XamlReaderHelper.DefinitionShared; info.LocalName = info.Name; info.RecordType = BamlRecordType.DefAttribute; info.Offset = dictKey.ValuePosition; return info; }
/***************************************************************************\ * * BamlReader.ProcessKeyTree * * Read a tree of baml records that make up a dictionary key and translate them * back in the compact syntax representation of a MarkupExtension section. * When we encounter KeyElementEnd record, then stop. * \***************************************************************************/ private BamlKeyInfo ProcessKeyTree() { BamlKeyElementStartRecord keyStartRecord = _currentBamlRecord as BamlKeyElementStartRecord; Debug.Assert(keyStartRecord != null, "Bad Key Element Start record"); // Translate the type information held in the baml record into // the "{prefix:Classname " format that would be used on // a x:Key attribute. BamlTypeInfoRecord typeInfo = MapTable.GetTypeInfoFromId(keyStartRecord.TypeId); string markupString = typeInfo.TypeFullName; markupString = markupString.Substring(markupString.LastIndexOf(".", StringComparison.Ordinal) + 1); string assemblyName; string prefix; string xmlNamespace; GetAssemblyAndPrefixAndXmlns(typeInfo, out assemblyName, out prefix, out xmlNamespace); if (prefix != string.Empty) { markupString = "{" + prefix + ":" + markupString + " "; } else { markupString = "{" + markupString + " "; } bool notDone = true; BamlNodeInfo nodeInfo; // Keep track of whether we have written a property or not at a given nesting // level so that we know when to add commas between properties. Also keep // track of when we have entered a constructor parameter section and when // we have written out the first parameter to handle adding commas between // constructor parameters. Stack readProperty = new Stack(); Stack readConstructor = new Stack(); Stack readFirstConstructor = new Stack(); readProperty.Push(false); // Property has not yet been read readConstructor.Push(false); // Constructor section has not been read readFirstConstructor.Push(false); // First constructor parameter has not been read while (notDone) { // Read the next record. Some of the processing below reads ahead one // record and sets _haveUnprocessedRecord to true, in which case we // don't want to read another one. if (!_haveUnprocessedRecord) { GetNextRecord(); } else { _haveUnprocessedRecord = false; } switch (_currentBamlRecord.RecordType) { // The following five records are internal to the BAMLReader and // are not exposed publicly. They are used to update the map table // that maps ids to assemblies, types and attributes. case BamlRecordType.AssemblyInfo: ReadAssemblyInfoRecord(); break; case BamlRecordType.TypeInfo: case BamlRecordType.TypeSerializerInfo: MapTable.LoadTypeInfoRecord((BamlTypeInfoRecord)_currentBamlRecord); break; case BamlRecordType.AttributeInfo: MapTable.LoadAttributeInfoRecord((BamlAttributeInfoRecord)_currentBamlRecord); break; case BamlRecordType.StringInfo: MapTable.LoadStringInfoRecord((BamlStringInfoRecord)_currentBamlRecord); break; case BamlRecordType.PropertyComplexStart: ReadPropertyComplexStartRecord(); nodeInfo = (BamlNodeInfo)_nodeStack.Pop(); if ((bool)readProperty.Pop()) { markupString += ", "; } markupString += nodeInfo.LocalName + "="; readProperty.Push(true); break; case BamlRecordType.PropertyComplexEnd: break; case BamlRecordType.Text: case BamlRecordType.TextWithId: BamlTextWithIdRecord textWithIdRecord = _currentBamlRecord as BamlTextWithIdRecord; if (textWithIdRecord != null) { // Get the value string from the string table, and cache it in the // record. textWithIdRecord.Value = MapTable.GetStringFromStringId( textWithIdRecord.ValueId); } // If the text contains '{' or '}' then we have to escape these // so that it won't be interpreted as a MarkupExtension string escapedString = EscapeString(((BamlTextRecord)_currentBamlRecord).Value); if ((bool)readFirstConstructor.Peek()) { markupString += ", "; } markupString += escapedString; if ((bool)readConstructor.Peek()) { readFirstConstructor.Pop(); readFirstConstructor.Push(true); } break; case BamlRecordType.ElementStart: // Process commas between constructor parameters if ((bool)readFirstConstructor.Peek()) { markupString += ", "; } if ((bool)readConstructor.Peek()) { readFirstConstructor.Pop(); readFirstConstructor.Push(true); } // Setup for the next level readProperty.Push(false); readConstructor.Push(false); readFirstConstructor.Push(false); // Write element type. Translate the type information held in the // baml record into the "prefix:Classname" format BamlElementStartRecord elementStartRecord = _currentBamlRecord as BamlElementStartRecord; BamlTypeInfoRecord elementTypeInfo = MapTable.GetTypeInfoFromId(elementStartRecord.TypeId); string typename = elementTypeInfo.TypeFullName; typename = typename.Substring(typename.LastIndexOf(".", StringComparison.Ordinal) + 1); GetAssemblyAndPrefixAndXmlns(elementTypeInfo, out assemblyName, out prefix, out xmlNamespace); if (prefix != string.Empty) { markupString += "{" + prefix + ":" + typename + " "; } else { markupString = "{" + typename + " "; } break; case BamlRecordType.ElementEnd: readProperty.Pop(); readConstructor.Pop(); readFirstConstructor.Pop(); markupString += "}"; break; case BamlRecordType.ConstructorParametersStart: readConstructor.Pop(); readConstructor.Push(true); break; case BamlRecordType.ConstructorParametersEnd: readConstructor.Pop(); readConstructor.Push(false); readFirstConstructor.Pop(); readFirstConstructor.Push(false); break; case BamlRecordType.ConstructorParameterType: // Process commas between constructor parameters if ((bool)readFirstConstructor.Peek()) { markupString += ", "; } if ((bool)readConstructor.Peek()) { readFirstConstructor.Pop(); readFirstConstructor.Push(true); } BamlConstructorParameterTypeRecord constTypeRecord = _currentBamlRecord as BamlConstructorParameterTypeRecord; markupString += GetTypeValueString(constTypeRecord.TypeId); break; case BamlRecordType.Property: case BamlRecordType.PropertyWithConverter: { string value = ((BamlPropertyRecord)_currentBamlRecord).Value; BamlPropertyInfo propertyInfo = ReadPropertyRecordCore(value); if ((bool)readProperty.Pop()) { markupString += ", "; } markupString += propertyInfo.LocalName + "=" + propertyInfo.Value; readProperty.Push(true); } break; case BamlRecordType.PropertyCustom: { BamlPropertyInfo propertyInfo = GetPropertyCustomRecordInfo(); if ((bool)readProperty.Pop()) { markupString += ", "; } markupString += propertyInfo.LocalName + "=" + propertyInfo.Value; readProperty.Push(true); } break; case BamlRecordType.PropertyStringReference: { string value = MapTable.GetStringFromStringId(((BamlPropertyStringReferenceRecord)_currentBamlRecord).StringId); BamlPropertyInfo propertyInfo = ReadPropertyRecordCore(value); if ((bool)readProperty.Pop()) { markupString += ", "; } markupString += propertyInfo.LocalName + "=" + propertyInfo.Value; readProperty.Push(true); } break; case BamlRecordType.PropertyTypeReference: { string value = GetTypeValueString(((BamlPropertyTypeReferenceRecord)_currentBamlRecord).TypeId); string attributeName = MapTable.GetAttributeNameFromId( ((BamlPropertyTypeReferenceRecord)_currentBamlRecord).AttributeId); if ((bool)readProperty.Pop()) { markupString += ", "; } markupString += attributeName + "=" + value; readProperty.Push(true); } break; case BamlRecordType.PropertyWithExtension: { string value = GetExtensionValueString((BamlPropertyWithExtensionRecord)_currentBamlRecord); string attributeName = MapTable.GetAttributeNameFromId( ((BamlPropertyWithExtensionRecord)_currentBamlRecord).AttributeId); if ((bool)readProperty.Pop()) { markupString += ", "; } markupString += attributeName + "=" + value; readProperty.Push(true); } break; case BamlRecordType.KeyElementEnd: markupString += "}"; notDone = false; _haveUnprocessedRecord = false; break; default: // Can't have any other type of record at this point. throw new InvalidOperationException(SR.Get(SRID.ParserUnknownBaml, ((int)_currentBamlRecord.RecordType).ToString(CultureInfo.CurrentCulture))); } } // At this point the markup string representing the MarkupExtension should // be complete, so set this as the value for this key. BamlKeyInfo info = new BamlKeyInfo(); info.Value = markupString; info.AssemblyName = string.Empty; info.Prefix = (string)_prefixDictionary[XamlReaderHelper.DefinitionNamespaceURI]; info.XmlNamespace = XamlReaderHelper.DefinitionNamespaceURI; info.ClrNamespace = string.Empty; info.Name = XamlReaderHelper.DefinitionName; info.LocalName = info.Name; info.RecordType = BamlRecordType.DefAttribute; info.Offset = ((IBamlDictionaryKey)keyStartRecord).ValuePosition; return info; }
/***************************************************************************\ * * BamlReader.ProcessDeferKey * * Read a single baml record. If it is a defer key, add it to the table of * keys. If we encounter something that is not a 'key', then set the *_haveUnprocessedRecord flag. * \***************************************************************************/ private void ProcessDeferKey() { switch (_currentBamlRecord.RecordType) { // The following three records are internal to the BAMLReader and // are not exposed publicly. They are used to update the map table // that maps ids to assemblies, types and attributes. case BamlRecordType.DefAttributeKeyString: BamlDefAttributeKeyStringRecord stringKeyRecord = _currentBamlRecord as BamlDefAttributeKeyStringRecord; if (stringKeyRecord != null) { BamlKeyInfo info; // The "Shared"ness is stored in the BAML with the Key // But at the XAML level it is a sibling attribute of the key. info = CheckForSharedness(); if (null != info) _deferKeys.Add(info); // Get the value string from the string table, and cache it in the // record. stringKeyRecord.Value = MapTable.GetStringFromStringId( stringKeyRecord.ValueId); // Add information to the key list to indicate we have a x:Key // attribute info = new BamlKeyInfo(); info.Value = stringKeyRecord.Value; info.AssemblyName = string.Empty; info.Prefix = (string)_prefixDictionary[XamlReaderHelper.DefinitionNamespaceURI]; info.XmlNamespace = XamlReaderHelper.DefinitionNamespaceURI; info.ClrNamespace = string.Empty; info.Name = XamlReaderHelper.DefinitionName; info.LocalName = info.Name; info.RecordType = BamlRecordType.DefAttribute; info.Offset = ((IBamlDictionaryKey)stringKeyRecord).ValuePosition; _deferKeys.Add(info); } break; case BamlRecordType.DefAttributeKeyType: BamlDefAttributeKeyTypeRecord typeKeyRecord = _currentBamlRecord as BamlDefAttributeKeyTypeRecord; if (typeKeyRecord != null) { // Translate the type information held in the baml record into // the {x:Type prefix:Classname} format that would be used on // a x:Key attribute. string typeExtensionPrefix = (string)_prefixDictionary[XamlReaderHelper.DefinitionNamespaceURI]; string typeExtensionName; if (typeExtensionPrefix != string.Empty) { typeExtensionName = "{" + typeExtensionPrefix + ":Type "; } else { typeExtensionName = "{Type "; } BamlTypeInfoRecord typeInfo = MapTable.GetTypeInfoFromId(typeKeyRecord.TypeId); string typeName = typeInfo.TypeFullName; typeName = typeName.Substring(typeName.LastIndexOf(".", StringComparison.Ordinal) + 1); string assemblyName; string prefix; string xmlNamespace; GetAssemblyAndPrefixAndXmlns(typeInfo, out assemblyName, out prefix, out xmlNamespace); if (prefix != string.Empty) { typeName = typeExtensionName + prefix + ":" + typeName + "}"; } else { typeName = typeExtensionName + typeName + "}"; } // Add information to the key list to indicate we have a x:Key // attribute BamlKeyInfo info = new BamlKeyInfo(); info.Value = typeName; info.AssemblyName = string.Empty; info.Prefix = typeExtensionPrefix; info.XmlNamespace = XamlReaderHelper.DefinitionNamespaceURI; info.ClrNamespace = string.Empty; info.Name = XamlReaderHelper.DefinitionName; info.LocalName = info.Name; info.RecordType = BamlRecordType.DefAttribute; info.Offset = ((IBamlDictionaryKey)typeKeyRecord).ValuePosition; _deferKeys.Add(info); } break; case BamlRecordType.KeyElementStart: { BamlKeyInfo info; // The "Shared"ness is stored in the BAML with the Key // But at the XAML level it is a sibling attribute of the key. info = CheckForSharedness(); if(null != info) _deferKeys.Add(info); // Process the subtree that is stored as part of a key tree and // translate this back into a compact MarkupExtension string. // Add information to the key list to indicate we have a x:Key // with a MarkupExtension info = ProcessKeyTree(); _deferKeys.Add(info); } break; case BamlRecordType.StaticResourceStart: case BamlRecordType.OptimizedStaticResource: { // Process the subtree stored as part of a StaticResource List<BamlRecord> srRecords = new List<BamlRecord>(); // This is for the start record _currentBamlRecord.Pin(); srRecords.Add(_currentBamlRecord); // Note that BamlOptmizedStaticResourceRecord is a singleton record if (_currentBamlRecord.RecordType == BamlRecordType.StaticResourceStart) { // Process the subtree that is stored as part of this static resource ProcessStaticResourceTree(srRecords); } // Add the current StaticResource record to the list of StaticResources held per key BamlKeyInfo keyInfo = _deferKeys[_deferKeys.Count-1]; keyInfo.StaticResources.Add(srRecords); } break; // Any other record types are not processed here default: _haveUnprocessedRecord = true; break; } }