Code:
/ Dotnetfx_Vista_SP2 / Dotnetfx_Vista_SP2 / 8.0.50727.4016 / DEVDIV / depot / DevDiv / releases / Orcas / QFE / wpf / src / Framework / MS / Internal / Globalization / BamlTreeMap.cs / 1 / BamlTreeMap.cs
//-------------------------------------------------------- // Class that implements BamlTreeMap. // // Created: Garyyang @ 12/1/2003 // //------------------------------------------------------- using System; using System.IO; using System.Xml; using System.Globalization; using System.Collections; using System.Collections.Generic; using System.Windows.Markup; using System.Windows.Markup.Localizer; using System.Diagnostics; using System.Text; using System.Windows; using MS.Utility; // Disabling 1634 and 1691: // In order to avoid generating warnings about unknown message numbers and // unknown pragmas when compiling C# source code with the C# compiler, // you need to disable warnings 1634 and 1691. (Presharp Documentation) #pragma warning disable 1634, 1691 namespace MS.Internal.Globalization { ////// creates mappings for the baml tree node /// this class in charges of creating mappings to the /// loclaizable resources and tree nodes. /// internal class BamlTreeMap { //---------------------------------- // internal Constructor //---------------------------------- ////// BamlTreeMap. /// internal BamlTreeMap( BamlLocalizer localizer, BamlTree tree, BamlLocalizabilityResolver resolver, TextReader comments ) { Debug.Assert(tree!= null, "Baml Tree is empty"); Debug.Assert(localizer!= null, "BamlLocalizer is null"); _tree = tree; // creates an internal resolver which willd delegate calls to client's resolver intelligently. _resolver = new InternalBamlLocalizabilityResolver(localizer, resolver, comments); // create a LocalizableResourceBuilder to build localizable resources _localizableResourceBuilder = new LocalizableResourceBuilder(_resolver); } //---------------------------------- // internal properties //---------------------------------- internal BamlLocalizationDictionary LocalizationDictionary { get { EnsureMap(); return _localizableResources; } } internal InternalBamlLocalizabilityResolver Resolver { get { return _resolver; } } ////// Maps a key to a baml tree node in the given tree /// internal BamlTreeNode MapKeyToBamlTreeNode(BamlLocalizableResourceKey key, BamlTree tree) { if (_keyToBamlNodeIndexMap.Contains(key)) { return tree[(int)_keyToBamlNodeIndexMap[key]]; } return null; } ////// Maps a uid to a baml tree node in the given tree /// internal BamlStartElementNode MapUidToBamlTreeElementNode(string uid, BamlTree tree) { if (_uidToBamlNodeIndexMap.Contains(uid)) { return tree[(int)_uidToBamlNodeIndexMap[uid]] as BamlStartElementNode; } return null; } //-------------------------------------- // Private methods //-------------------------------------- // construct the maps for enumeration internal void EnsureMap() { if (_localizableResources != null) return; // map is already created. // create the table based on the treesize passed in // the hashtable is for look-up during update _resolver.InitLocalizabilityCache(); _keyToBamlNodeIndexMap = new Hashtable(_tree.Size); _uidToBamlNodeIndexMap = new Hashtable(_tree.Size / 2); _localizableResources = new BamlLocalizationDictionary(); for (int i = 0; i < _tree.Size; i++) { BamlTreeNode currentNode = _tree[i]; // a node may be marked as unidentifiable if it or its parent has a duplicate uid. if (currentNode.Unidentifiable) continue; // skip unidentifiable nodes if (currentNode.NodeType == BamlNodeType.StartElement) { // remember classes encountered in this baml BamlStartElementNode elementNode = (BamlStartElementNode) currentNode; _resolver.AddClassAndAssembly(elementNode.TypeFullName, elementNode.AssemblyName); } // find the Uid of the current node BamlLocalizableResourceKey key = GetKey(currentNode); if (key != null) { if (currentNode.NodeType == BamlNodeType.StartElement) { // store uid mapping to the corresponding element node if (_uidToBamlNodeIndexMap.ContainsKey(key.Uid)) { _resolver.RaiseErrorNotifyEvent( new BamlLocalizerErrorNotifyEventArgs( key, BamlLocalizerError.DuplicateUid ) ); // Mark this element and its properties unidentifiable. currentNode.Unidentifiable = true; if (currentNode.Children != null) { foreach (BamlTreeNode child in currentNode.Children) { if (child.NodeType != BamlNodeType.StartElement) child.Unidentifiable = true; } } continue; // skip the duplicate node } else { _uidToBamlNodeIndexMap.Add(key.Uid, i); } } _keyToBamlNodeIndexMap.Add(key, i); if (_localizableResources.RootElementKey == null && currentNode.NodeType == BamlNodeType.StartElement && currentNode.Parent != null && currentNode.Parent.NodeType == BamlNodeType.StartDocument) { // remember the key to the root element so that // users can further add modifications to the root that would have a global impact. // such as FlowDirection or CultureInfo _localizableResources.SetRootElementKey(key); } // create the resource and add to the dictionary BamlLocalizableResource resource = _localizableResourceBuilder.BuildFromNode(key, currentNode); if (resource != null) { _localizableResources.Add(key, resource); } } } _resolver.ReleaseLocalizabilityCache(); } //------------------------------------------------- // Internal static //------------------------------------------------- ////// Return the localizable resource key for this baml tree node. /// If this node shouldn't be localized, the key returned will be null. /// internal static BamlLocalizableResourceKey GetKey(BamlTreeNode node) { BamlLocalizableResourceKey key = null; switch (node.NodeType) { case BamlNodeType.StartElement: { BamlStartElementNode elementNode = (BamlStartElementNode) node; if (elementNode.Uid != null) { key = new BamlLocalizableResourceKey( elementNode.Uid, elementNode.TypeFullName, BamlConst.ContentSuffix, elementNode.AssemblyName ); } break; } case BamlNodeType.Property: { BamlPropertyNode propertyNode = (BamlPropertyNode) node; BamlStartElementNode parent = (BamlStartElementNode) propertyNode.Parent; if (parent.Uid != null) { string uid; if (propertyNode.Index <= 0) { uid = parent.Uid; } else { // This node is auto-numbered. This has to do with the fact that // the compiler may compile duplicated properties into Baml under the same element. uid = string.Format( XamlSerializerUtil.EnglishUSCulture, "{0}.{1}_{2}", parent.Uid, propertyNode.PropertyName, propertyNode.Index ); } key = new BamlLocalizableResourceKey( uid, propertyNode.OwnerTypeFullName, propertyNode.PropertyName, propertyNode.AssemblyName ); } break; } case BamlNodeType.LiteralContent: { BamlLiteralContentNode literalNode = (BamlLiteralContentNode) node; BamlStartElementNode parent = (BamlStartElementNode) node.Parent; if (parent.Uid != null) { key = new BamlLocalizableResourceKey( parent.Uid, parent.TypeFullName, BamlConst.LiteralContentSuffix, parent.AssemblyName ); } break; } } return key; } //--------------------------------- // private members //--------------------------------- private Hashtable _keyToBamlNodeIndexMap; // _key to baml node. Key is integer. Not using Generic on value type for perf reason private Hashtable _uidToBamlNodeIndexMap; private LocalizableResourceBuilder _localizableResourceBuilder; private BamlLocalizationDictionary _localizableResources; private BamlTree _tree; private InternalBamlLocalizabilityResolver _resolver; } internal class InternalBamlLocalizabilityResolver : BamlLocalizabilityResolver { BamlLocalizabilityResolver _externalResolver; // a list of assemblies encounter in the baml FrugalObjectList_assemblyNames; // class name mapped to assembly index in the Frugal list Hashtable _classNameToAssemblyIndex; // cached localizablity values private Dictionary _classAttributeTable; private Dictionary _propertyAttributeTable; // // cached localization comments // Normally, we only need to use comments of a single element at one time. // In case of formatting inline tags we might be grabing comments of a few more elements. // A small cached would be enough // private ElementComments[] _comments; private int _commentsIndex; // Localization comment document private XmlDocument _commentsDocument; private BamlLocalizer _localizer; private TextReader _commentingText; internal InternalBamlLocalizabilityResolver( BamlLocalizer localizer, BamlLocalizabilityResolver externalResolver, TextReader comments ) { _localizer = localizer; _externalResolver = externalResolver; _commentingText = comments; } internal void AddClassAndAssembly(string className, string assemblyName) { if (assemblyName == null || _classNameToAssemblyIndex.Contains(className)) return; int index = _assemblyNames.IndexOf(assemblyName); if (index < 0) { // add the new assembly _assemblyNames.Add(assemblyName); index = _assemblyNames.Count - 1; } _classNameToAssemblyIndex.Add(className, index); } internal void InitLocalizabilityCache() { _assemblyNames = new FrugalObjectList (); _classNameToAssemblyIndex = new Hashtable(8); _classAttributeTable = new Dictionary (8); _propertyAttributeTable = new Dictionary (8); // 8 cached values for comments. Slots are reused in round-robin fashion _comments = new ElementComments[8]; _commentsIndex = 0; XmlDocument doc = null; if (_commentingText != null) { doc = new XmlDocument(); try { doc.Load(_commentingText); }catch (XmlException) { RaiseErrorNotifyEvent( new BamlLocalizerErrorNotifyEventArgs( new BamlLocalizableResourceKey(string.Empty, string.Empty, string.Empty), BamlLocalizerError.InvalidCommentingXml ) ); doc = null; } } _commentsDocument = doc; } internal void ReleaseLocalizabilityCache() { // release cached that is not needed for baml genearation _propertyAttributeTable = null; _comments = null; _commentsIndex = 0; _commentsDocument = null; } // Grab the comments on a BamlTreeNode for the value. internal LocalizabilityGroup GetLocalizabilityComment( BamlStartElementNode node, string localName ) { // get all the comments declares on this node ElementComments comment = LookupCommentForElement(node); for (int i = 0; i < comment.LocalizationAttributes.Length; i++) { if (comment.LocalizationAttributes[i].PropertyName == localName) { return (LocalizabilityGroup) comment.LocalizationAttributes[i].Value; } } return null; } internal string GetStringComment( BamlStartElementNode node, string localName ) { // get all the comments declares on this node ElementComments comment = LookupCommentForElement(node); for (int i = 0; i < comment.LocalizationComments.Length; i++) { if (comment.LocalizationComments[i].PropertyName == localName) { return (string) comment.LocalizationComments[i].Value; } } return null; } internal void RaiseErrorNotifyEvent(BamlLocalizerErrorNotifyEventArgs e) { _localizer.RaiseErrorNotifyEvent(e); } //-------------------------------------- // BamlLocalizabilityResolver interface //-------------------------------------- public override ElementLocalizability GetElementLocalizability(string assembly, string className) { if ( _externalResolver == null || assembly == null || assembly.Length == 0 || className == null || className.Length == 0) { // return the default value return new ElementLocalizability( null, DefaultAttribute ); } if (_classAttributeTable.ContainsKey(className)) { // return cached value return _classAttributeTable[className]; } else { ElementLocalizability loc = _externalResolver.GetElementLocalizability(assembly, className); if (loc == null || loc.Attribute == null) { loc = new ElementLocalizability( null, DefaultAttribute ); } _classAttributeTable[className] = loc; return loc; } } public override LocalizabilityAttribute GetPropertyLocalizability(string assembly, string className, string property) { if ( _externalResolver == null || assembly == null || assembly.Length == 0 || className == null || className.Length == 0 || property == null || property.Length == 0) { return DefaultAttribute; } string fullName = className + ":" + property; if (_propertyAttributeTable.ContainsKey(fullName)) { // return cached value return _propertyAttributeTable[fullName]; } else { LocalizabilityAttribute loc = _externalResolver.GetPropertyLocalizability(assembly, className, property); if (loc == null) { loc = DefaultAttribute; } _propertyAttributeTable[fullName] = loc; return loc; } } public override string ResolveFormattingTagToClass(string formattingTag) { // go through the cache to find the mapping foreach (KeyValuePair pair in _classAttributeTable) { if (pair.Value.FormattingTag == formattingTag) return pair.Key; } string className = null; if (_externalResolver != null) { // it is a formatting tag not resolved before. need to ask for client's help className = _externalResolver.ResolveFormattingTagToClass(formattingTag); if (!string.IsNullOrEmpty(className)) { // cache the result if (_classAttributeTable.ContainsKey(className)) { _classAttributeTable[className].FormattingTag = formattingTag; } else { _classAttributeTable[className] = new ElementLocalizability(formattingTag, null); } } } return className; } public override string ResolveAssemblyFromClass(string className) { if (className == null || className.Length == 0) { return string.Empty; } // go through class encountered in this baml first // if we saw this class before, we return the corresponding assembly if (_classNameToAssemblyIndex.Contains(className)) { return _assemblyNames[(int)_classNameToAssemblyIndex[className]]; } string assemblyName = null; if (_externalResolver != null) { // it is a class not resolved before. need to ask for client's help assemblyName = _externalResolver.ResolveAssemblyFromClass(className); AddClassAndAssembly(className, assemblyName); } return assemblyName; } private LocalizabilityAttribute DefaultAttribute { get { // if the value has no localizability attribute set, we default to all inherit. LocalizabilityAttribute attribute = new LocalizabilityAttribute(LocalizationCategory.Inherit); attribute.Modifiability = Modifiability.Inherit; attribute.Readability = Readability.Inherit; return attribute; } } private ElementComments LookupCommentForElement(BamlStartElementNode node) { Debug.Assert(node.NodeType == BamlNodeType.StartElement); if (node.Uid == null) { return new ElementComments(); // return empty comments for null Uid } for (int i = 0; i < _comments.Length; i++) { if (_comments[i] != null && _comments[i].ElementId == node.Uid) { return _comments[i]; } } ElementComments comment = new ElementComments(); comment.ElementId = node.Uid; if (_commentsDocument != null) { // select the xmlNode containing the comments XmlElement element = FindElementByID(_commentsDocument, node.Uid); if (element != null) { // parse the comments string locAttribute = element.GetAttribute(LocComments.LocLocalizabilityAttribute); SetLocalizationAttributes(node, comment, locAttribute); locAttribute = element.GetAttribute(LocComments.LocCommentsAttribute); SetLocalizationComments(node, comment, locAttribute); } } if (node.Children != null) { // // The baml itself might contain comments too // Grab the missing comments from Baml if there is any. // for (int i = 0; i < node.Children.Count && (comment.LocalizationComments.Length == 0 || comment.LocalizationAttributes.Length == 0); i++) { BamlTreeNode child = (BamlTreeNode) node.Children[i]; if (child.NodeType == BamlNodeType.Property) { BamlPropertyNode propertyNode = (BamlPropertyNode) child; if (LocComments.IsLocCommentsProperty(propertyNode.OwnerTypeFullName, propertyNode.PropertyName) && comment.LocalizationComments.Length == 0) { // grab comments from Baml SetLocalizationComments(node, comment, propertyNode.Value); } else if (LocComments.IsLocLocalizabilityProperty(propertyNode.OwnerTypeFullName, propertyNode.PropertyName) && comment.LocalizationAttributes.Length == 0) { // grab comments from Baml SetLocalizationAttributes(node, comment, propertyNode.Value); } } } } // cached it _comments[_commentsIndex] = comment; _commentsIndex = (_commentsIndex + 1) % _comments.Length; return comment; } private static XmlElement FindElementByID(XmlDocument doc, string uid) { // Have considered using XPATH. However, XPATH doesn't have a way to escape single quote within // single quotes, here we iterate through the document by ourselves if (doc != null && doc.DocumentElement != null) { foreach(XmlNode node in doc.DocumentElement.ChildNodes) { if (node.NodeType == XmlNodeType.Element) { XmlElement element = (XmlElement) node; if ( element.Name == LocComments.LocCommentsElement && element.GetAttribute(LocComments.LocCommentIDAttribute) == uid) { return element; } } } } return null; } private void SetLocalizationAttributes( BamlStartElementNode node, ElementComments comments, string attributes ) { if (!string.IsNullOrEmpty(attributes)) { try { comments.LocalizationAttributes = LocComments.ParsePropertyLocalizabilityAttributes(attributes); } catch (FormatException) { RaiseErrorNotifyEvent( new BamlLocalizerErrorNotifyEventArgs( BamlTreeMap.GetKey(node), BamlLocalizerError.InvalidLocalizationAttributes ) ); } } } private void SetLocalizationComments( BamlStartElementNode node, ElementComments comments, string stringComment ) { if (!string.IsNullOrEmpty(stringComment)) { try { comments.LocalizationComments = LocComments.ParsePropertyComments(stringComment); } catch (FormatException) { RaiseErrorNotifyEvent( new BamlLocalizerErrorNotifyEventArgs( BamlTreeMap.GetKey(node), BamlLocalizerError.InvalidLocalizationComments ) ); } } } /// /// Data structure for all the comments declared on a particular element /// private class ElementComments { internal string ElementId; // element's uid internal PropertyComment[] LocalizationAttributes; // Localization.Attributes internal PropertyComment[] LocalizationComments; // Localization.Comments internal ElementComments() { ElementId = null; LocalizationAttributes = new PropertyComment[0]; LocalizationComments = new PropertyComment[0]; } } } } // File provided for Reference Use Only by Microsoft Corporation (c) 2007. // Copyright (c) Microsoft Corporation. All rights reserved. //-------------------------------------------------------- // Class that implements BamlTreeMap. // // Created: Garyyang @ 12/1/2003 // //------------------------------------------------------- using System; using System.IO; using System.Xml; using System.Globalization; using System.Collections; using System.Collections.Generic; using System.Windows.Markup; using System.Windows.Markup.Localizer; using System.Diagnostics; using System.Text; using System.Windows; using MS.Utility; // Disabling 1634 and 1691: // In order to avoid generating warnings about unknown message numbers and // unknown pragmas when compiling C# source code with the C# compiler, // you need to disable warnings 1634 and 1691. (Presharp Documentation) #pragma warning disable 1634, 1691 namespace MS.Internal.Globalization { ////// creates mappings for the baml tree node /// this class in charges of creating mappings to the /// loclaizable resources and tree nodes. /// internal class BamlTreeMap { //---------------------------------- // internal Constructor //---------------------------------- ////// BamlTreeMap. /// internal BamlTreeMap( BamlLocalizer localizer, BamlTree tree, BamlLocalizabilityResolver resolver, TextReader comments ) { Debug.Assert(tree!= null, "Baml Tree is empty"); Debug.Assert(localizer!= null, "BamlLocalizer is null"); _tree = tree; // creates an internal resolver which willd delegate calls to client's resolver intelligently. _resolver = new InternalBamlLocalizabilityResolver(localizer, resolver, comments); // create a LocalizableResourceBuilder to build localizable resources _localizableResourceBuilder = new LocalizableResourceBuilder(_resolver); } //---------------------------------- // internal properties //---------------------------------- internal BamlLocalizationDictionary LocalizationDictionary { get { EnsureMap(); return _localizableResources; } } internal InternalBamlLocalizabilityResolver Resolver { get { return _resolver; } } ////// Maps a key to a baml tree node in the given tree /// internal BamlTreeNode MapKeyToBamlTreeNode(BamlLocalizableResourceKey key, BamlTree tree) { if (_keyToBamlNodeIndexMap.Contains(key)) { return tree[(int)_keyToBamlNodeIndexMap[key]]; } return null; } ////// Maps a uid to a baml tree node in the given tree /// internal BamlStartElementNode MapUidToBamlTreeElementNode(string uid, BamlTree tree) { if (_uidToBamlNodeIndexMap.Contains(uid)) { return tree[(int)_uidToBamlNodeIndexMap[uid]] as BamlStartElementNode; } return null; } //-------------------------------------- // Private methods //-------------------------------------- // construct the maps for enumeration internal void EnsureMap() { if (_localizableResources != null) return; // map is already created. // create the table based on the treesize passed in // the hashtable is for look-up during update _resolver.InitLocalizabilityCache(); _keyToBamlNodeIndexMap = new Hashtable(_tree.Size); _uidToBamlNodeIndexMap = new Hashtable(_tree.Size / 2); _localizableResources = new BamlLocalizationDictionary(); for (int i = 0; i < _tree.Size; i++) { BamlTreeNode currentNode = _tree[i]; // a node may be marked as unidentifiable if it or its parent has a duplicate uid. if (currentNode.Unidentifiable) continue; // skip unidentifiable nodes if (currentNode.NodeType == BamlNodeType.StartElement) { // remember classes encountered in this baml BamlStartElementNode elementNode = (BamlStartElementNode) currentNode; _resolver.AddClassAndAssembly(elementNode.TypeFullName, elementNode.AssemblyName); } // find the Uid of the current node BamlLocalizableResourceKey key = GetKey(currentNode); if (key != null) { if (currentNode.NodeType == BamlNodeType.StartElement) { // store uid mapping to the corresponding element node if (_uidToBamlNodeIndexMap.ContainsKey(key.Uid)) { _resolver.RaiseErrorNotifyEvent( new BamlLocalizerErrorNotifyEventArgs( key, BamlLocalizerError.DuplicateUid ) ); // Mark this element and its properties unidentifiable. currentNode.Unidentifiable = true; if (currentNode.Children != null) { foreach (BamlTreeNode child in currentNode.Children) { if (child.NodeType != BamlNodeType.StartElement) child.Unidentifiable = true; } } continue; // skip the duplicate node } else { _uidToBamlNodeIndexMap.Add(key.Uid, i); } } _keyToBamlNodeIndexMap.Add(key, i); if (_localizableResources.RootElementKey == null && currentNode.NodeType == BamlNodeType.StartElement && currentNode.Parent != null && currentNode.Parent.NodeType == BamlNodeType.StartDocument) { // remember the key to the root element so that // users can further add modifications to the root that would have a global impact. // such as FlowDirection or CultureInfo _localizableResources.SetRootElementKey(key); } // create the resource and add to the dictionary BamlLocalizableResource resource = _localizableResourceBuilder.BuildFromNode(key, currentNode); if (resource != null) { _localizableResources.Add(key, resource); } } } _resolver.ReleaseLocalizabilityCache(); } //------------------------------------------------- // Internal static //------------------------------------------------- ////// Return the localizable resource key for this baml tree node. /// If this node shouldn't be localized, the key returned will be null. /// internal static BamlLocalizableResourceKey GetKey(BamlTreeNode node) { BamlLocalizableResourceKey key = null; switch (node.NodeType) { case BamlNodeType.StartElement: { BamlStartElementNode elementNode = (BamlStartElementNode) node; if (elementNode.Uid != null) { key = new BamlLocalizableResourceKey( elementNode.Uid, elementNode.TypeFullName, BamlConst.ContentSuffix, elementNode.AssemblyName ); } break; } case BamlNodeType.Property: { BamlPropertyNode propertyNode = (BamlPropertyNode) node; BamlStartElementNode parent = (BamlStartElementNode) propertyNode.Parent; if (parent.Uid != null) { string uid; if (propertyNode.Index <= 0) { uid = parent.Uid; } else { // This node is auto-numbered. This has to do with the fact that // the compiler may compile duplicated properties into Baml under the same element. uid = string.Format( XamlSerializerUtil.EnglishUSCulture, "{0}.{1}_{2}", parent.Uid, propertyNode.PropertyName, propertyNode.Index ); } key = new BamlLocalizableResourceKey( uid, propertyNode.OwnerTypeFullName, propertyNode.PropertyName, propertyNode.AssemblyName ); } break; } case BamlNodeType.LiteralContent: { BamlLiteralContentNode literalNode = (BamlLiteralContentNode) node; BamlStartElementNode parent = (BamlStartElementNode) node.Parent; if (parent.Uid != null) { key = new BamlLocalizableResourceKey( parent.Uid, parent.TypeFullName, BamlConst.LiteralContentSuffix, parent.AssemblyName ); } break; } } return key; } //--------------------------------- // private members //--------------------------------- private Hashtable _keyToBamlNodeIndexMap; // _key to baml node. Key is integer. Not using Generic on value type for perf reason private Hashtable _uidToBamlNodeIndexMap; private LocalizableResourceBuilder _localizableResourceBuilder; private BamlLocalizationDictionary _localizableResources; private BamlTree _tree; private InternalBamlLocalizabilityResolver _resolver; } internal class InternalBamlLocalizabilityResolver : BamlLocalizabilityResolver { BamlLocalizabilityResolver _externalResolver; // a list of assemblies encounter in the baml FrugalObjectList_assemblyNames; // class name mapped to assembly index in the Frugal list Hashtable _classNameToAssemblyIndex; // cached localizablity values private Dictionary _classAttributeTable; private Dictionary _propertyAttributeTable; // // cached localization comments // Normally, we only need to use comments of a single element at one time. // In case of formatting inline tags we might be grabing comments of a few more elements. // A small cached would be enough // private ElementComments[] _comments; private int _commentsIndex; // Localization comment document private XmlDocument _commentsDocument; private BamlLocalizer _localizer; private TextReader _commentingText; internal InternalBamlLocalizabilityResolver( BamlLocalizer localizer, BamlLocalizabilityResolver externalResolver, TextReader comments ) { _localizer = localizer; _externalResolver = externalResolver; _commentingText = comments; } internal void AddClassAndAssembly(string className, string assemblyName) { if (assemblyName == null || _classNameToAssemblyIndex.Contains(className)) return; int index = _assemblyNames.IndexOf(assemblyName); if (index < 0) { // add the new assembly _assemblyNames.Add(assemblyName); index = _assemblyNames.Count - 1; } _classNameToAssemblyIndex.Add(className, index); } internal void InitLocalizabilityCache() { _assemblyNames = new FrugalObjectList (); _classNameToAssemblyIndex = new Hashtable(8); _classAttributeTable = new Dictionary (8); _propertyAttributeTable = new Dictionary (8); // 8 cached values for comments. Slots are reused in round-robin fashion _comments = new ElementComments[8]; _commentsIndex = 0; XmlDocument doc = null; if (_commentingText != null) { doc = new XmlDocument(); try { doc.Load(_commentingText); }catch (XmlException) { RaiseErrorNotifyEvent( new BamlLocalizerErrorNotifyEventArgs( new BamlLocalizableResourceKey(string.Empty, string.Empty, string.Empty), BamlLocalizerError.InvalidCommentingXml ) ); doc = null; } } _commentsDocument = doc; } internal void ReleaseLocalizabilityCache() { // release cached that is not needed for baml genearation _propertyAttributeTable = null; _comments = null; _commentsIndex = 0; _commentsDocument = null; } // Grab the comments on a BamlTreeNode for the value. internal LocalizabilityGroup GetLocalizabilityComment( BamlStartElementNode node, string localName ) { // get all the comments declares on this node ElementComments comment = LookupCommentForElement(node); for (int i = 0; i < comment.LocalizationAttributes.Length; i++) { if (comment.LocalizationAttributes[i].PropertyName == localName) { return (LocalizabilityGroup) comment.LocalizationAttributes[i].Value; } } return null; } internal string GetStringComment( BamlStartElementNode node, string localName ) { // get all the comments declares on this node ElementComments comment = LookupCommentForElement(node); for (int i = 0; i < comment.LocalizationComments.Length; i++) { if (comment.LocalizationComments[i].PropertyName == localName) { return (string) comment.LocalizationComments[i].Value; } } return null; } internal void RaiseErrorNotifyEvent(BamlLocalizerErrorNotifyEventArgs e) { _localizer.RaiseErrorNotifyEvent(e); } //-------------------------------------- // BamlLocalizabilityResolver interface //-------------------------------------- public override ElementLocalizability GetElementLocalizability(string assembly, string className) { if ( _externalResolver == null || assembly == null || assembly.Length == 0 || className == null || className.Length == 0) { // return the default value return new ElementLocalizability( null, DefaultAttribute ); } if (_classAttributeTable.ContainsKey(className)) { // return cached value return _classAttributeTable[className]; } else { ElementLocalizability loc = _externalResolver.GetElementLocalizability(assembly, className); if (loc == null || loc.Attribute == null) { loc = new ElementLocalizability( null, DefaultAttribute ); } _classAttributeTable[className] = loc; return loc; } } public override LocalizabilityAttribute GetPropertyLocalizability(string assembly, string className, string property) { if ( _externalResolver == null || assembly == null || assembly.Length == 0 || className == null || className.Length == 0 || property == null || property.Length == 0) { return DefaultAttribute; } string fullName = className + ":" + property; if (_propertyAttributeTable.ContainsKey(fullName)) { // return cached value return _propertyAttributeTable[fullName]; } else { LocalizabilityAttribute loc = _externalResolver.GetPropertyLocalizability(assembly, className, property); if (loc == null) { loc = DefaultAttribute; } _propertyAttributeTable[fullName] = loc; return loc; } } public override string ResolveFormattingTagToClass(string formattingTag) { // go through the cache to find the mapping foreach (KeyValuePair pair in _classAttributeTable) { if (pair.Value.FormattingTag == formattingTag) return pair.Key; } string className = null; if (_externalResolver != null) { // it is a formatting tag not resolved before. need to ask for client's help className = _externalResolver.ResolveFormattingTagToClass(formattingTag); if (!string.IsNullOrEmpty(className)) { // cache the result if (_classAttributeTable.ContainsKey(className)) { _classAttributeTable[className].FormattingTag = formattingTag; } else { _classAttributeTable[className] = new ElementLocalizability(formattingTag, null); } } } return className; } public override string ResolveAssemblyFromClass(string className) { if (className == null || className.Length == 0) { return string.Empty; } // go through class encountered in this baml first // if we saw this class before, we return the corresponding assembly if (_classNameToAssemblyIndex.Contains(className)) { return _assemblyNames[(int)_classNameToAssemblyIndex[className]]; } string assemblyName = null; if (_externalResolver != null) { // it is a class not resolved before. need to ask for client's help assemblyName = _externalResolver.ResolveAssemblyFromClass(className); AddClassAndAssembly(className, assemblyName); } return assemblyName; } private LocalizabilityAttribute DefaultAttribute { get { // if the value has no localizability attribute set, we default to all inherit. LocalizabilityAttribute attribute = new LocalizabilityAttribute(LocalizationCategory.Inherit); attribute.Modifiability = Modifiability.Inherit; attribute.Readability = Readability.Inherit; return attribute; } } private ElementComments LookupCommentForElement(BamlStartElementNode node) { Debug.Assert(node.NodeType == BamlNodeType.StartElement); if (node.Uid == null) { return new ElementComments(); // return empty comments for null Uid } for (int i = 0; i < _comments.Length; i++) { if (_comments[i] != null && _comments[i].ElementId == node.Uid) { return _comments[i]; } } ElementComments comment = new ElementComments(); comment.ElementId = node.Uid; if (_commentsDocument != null) { // select the xmlNode containing the comments XmlElement element = FindElementByID(_commentsDocument, node.Uid); if (element != null) { // parse the comments string locAttribute = element.GetAttribute(LocComments.LocLocalizabilityAttribute); SetLocalizationAttributes(node, comment, locAttribute); locAttribute = element.GetAttribute(LocComments.LocCommentsAttribute); SetLocalizationComments(node, comment, locAttribute); } } if (node.Children != null) { // // The baml itself might contain comments too // Grab the missing comments from Baml if there is any. // for (int i = 0; i < node.Children.Count && (comment.LocalizationComments.Length == 0 || comment.LocalizationAttributes.Length == 0); i++) { BamlTreeNode child = (BamlTreeNode) node.Children[i]; if (child.NodeType == BamlNodeType.Property) { BamlPropertyNode propertyNode = (BamlPropertyNode) child; if (LocComments.IsLocCommentsProperty(propertyNode.OwnerTypeFullName, propertyNode.PropertyName) && comment.LocalizationComments.Length == 0) { // grab comments from Baml SetLocalizationComments(node, comment, propertyNode.Value); } else if (LocComments.IsLocLocalizabilityProperty(propertyNode.OwnerTypeFullName, propertyNode.PropertyName) && comment.LocalizationAttributes.Length == 0) { // grab comments from Baml SetLocalizationAttributes(node, comment, propertyNode.Value); } } } } // cached it _comments[_commentsIndex] = comment; _commentsIndex = (_commentsIndex + 1) % _comments.Length; return comment; } private static XmlElement FindElementByID(XmlDocument doc, string uid) { // Have considered using XPATH. However, XPATH doesn't have a way to escape single quote within // single quotes, here we iterate through the document by ourselves if (doc != null && doc.DocumentElement != null) { foreach(XmlNode node in doc.DocumentElement.ChildNodes) { if (node.NodeType == XmlNodeType.Element) { XmlElement element = (XmlElement) node; if ( element.Name == LocComments.LocCommentsElement && element.GetAttribute(LocComments.LocCommentIDAttribute) == uid) { return element; } } } } return null; } private void SetLocalizationAttributes( BamlStartElementNode node, ElementComments comments, string attributes ) { if (!string.IsNullOrEmpty(attributes)) { try { comments.LocalizationAttributes = LocComments.ParsePropertyLocalizabilityAttributes(attributes); } catch (FormatException) { RaiseErrorNotifyEvent( new BamlLocalizerErrorNotifyEventArgs( BamlTreeMap.GetKey(node), BamlLocalizerError.InvalidLocalizationAttributes ) ); } } } private void SetLocalizationComments( BamlStartElementNode node, ElementComments comments, string stringComment ) { if (!string.IsNullOrEmpty(stringComment)) { try { comments.LocalizationComments = LocComments.ParsePropertyComments(stringComment); } catch (FormatException) { RaiseErrorNotifyEvent( new BamlLocalizerErrorNotifyEventArgs( BamlTreeMap.GetKey(node), BamlLocalizerError.InvalidLocalizationComments ) ); } } } /// /// Data structure for all the comments declared on a particular element /// private class ElementComments { internal string ElementId; // element's uid internal PropertyComment[] LocalizationAttributes; // Localization.Attributes internal PropertyComment[] LocalizationComments; // Localization.Comments internal ElementComments() { ElementId = null; LocalizationAttributes = new PropertyComment[0]; LocalizationComments = new PropertyComment[0]; } } } } // 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
- ArgIterator.cs
- RuleInfoComparer.cs
- SpecialNameAttribute.cs
- TypeListConverter.cs
- ObjectListItemCollection.cs
- SizeConverter.cs
- QueryCursorEventArgs.cs
- ZipIOExtraFieldZip64Element.cs
- TableCellCollection.cs
- UIElementAutomationPeer.cs
- CodeComment.cs
- WebPartConnectionsCloseVerb.cs
- ConfigurationElementProperty.cs
- MenuAdapter.cs
- ListViewGroup.cs
- ReferencedType.cs
- RuleRef.cs
- XPathDocumentNavigator.cs
- DesignTimeType.cs
- BindingSource.cs
- AuthenticationConfig.cs
- SynchronizedMessageSource.cs
- FormViewModeEventArgs.cs
- XmlCharCheckingWriter.cs
- SafeBitVector32.cs
- SaveFileDialog.cs
- MenuEventArgs.cs
- ListViewHitTestInfo.cs
- DataGridViewColumnConverter.cs
- ProcessInfo.cs
- BamlLocalizer.cs
- FixedSOMTableCell.cs
- PaperSize.cs
- ColumnMapTranslator.cs
- ReadOnlyDataSource.cs
- sqlpipe.cs
- ClientRoleProvider.cs
- FunctionDetailsReader.cs
- DataContract.cs
- Column.cs
- RtfToXamlLexer.cs
- WebResourceUtil.cs
- TraceFilter.cs
- InkCanvasAutomationPeer.cs
- CorrelationResolver.cs
- VisualBrush.cs
- HashAlgorithm.cs
- DataObject.cs
- HttpListenerPrefixCollection.cs
- listitem.cs
- SymmetricCryptoHandle.cs
- ContainerFilterService.cs
- SapiAttributeParser.cs
- FormClosedEvent.cs
- SchemaImporter.cs
- LocatorPart.cs
- MutableAssemblyCacheEntry.cs
- HorizontalAlignConverter.cs
- ReadWriteObjectLock.cs
- DbModificationCommandTree.cs
- CommonDialog.cs
- ellipse.cs
- SqlDataSourceView.cs
- ControlParameter.cs
- Configuration.cs
- RegistrationServices.cs
- TripleDESCryptoServiceProvider.cs
- COMException.cs
- Repeater.cs
- ScriptControl.cs
- PermissionAttributes.cs
- RedistVersionInfo.cs
- DateTimeOffsetStorage.cs
- ExpressionBuilderContext.cs
- StateMachineSubscription.cs
- RowTypeElement.cs
- XmlReader.cs
- Utils.cs
- SchemaAttDef.cs
- ParagraphResult.cs
- StreamWriter.cs
- TableChangeProcessor.cs
- ProcessHostServerConfig.cs
- ToolBarButton.cs
- DBSchemaTable.cs
- ResourceFallbackManager.cs
- URLAttribute.cs
- BoolExpressionVisitors.cs
- BamlLocalizableResource.cs
- WebEventCodes.cs
- TextServicesPropertyRanges.cs
- MbpInfo.cs
- XmlFormatWriterGenerator.cs
- DataGridCell.cs
- UniformGrid.cs
- TeredoHelper.cs
- AnnotationHighlightLayer.cs
- sqlstateclientmanager.cs
- FontInfo.cs
- StrongNameUtility.cs