Code:
/ DotNET / DotNET / 8.0 / untmp / whidbey / REDBITS / ndp / fx / src / Designer / CompMod / System / ComponentModel / Design / Serialization / ResourceCodeDomSerializer.cs / 1 / ResourceCodeDomSerializer.cs
//------------------------------------------------------------------------------ //// Copyright (c) Microsoft Corporation. All rights reserved. // //----------------------------------------------------------------------------- /* */ namespace System.ComponentModel.Design.Serialization { using System; using System.CodeDom; using System.Collections; using System.Collections.Generic; using System.ComponentModel; using System.ComponentModel.Design; using System.Design; using System.Diagnostics; using System.Globalization; using System.IO; using System.Reflection; using System.Resources; using System.Runtime.Serialization; ////// /// Code model serializer for resource managers. This is called /// in one of two ways. On Deserialization, we are associated /// with a ResourceManager object. Instead of creating a /// ResourceManager, however, we create an object called a /// SerializationResourceManager. This class inherits /// from ResourceManager, but overrides all of the methods. /// Instead of letting resource manager maintain resource /// sets, it uses the designer host's IResourceService /// for this purpose. /// /// During serialization, this class will also create /// a SerializationResourceManager. This will be added /// to the serialization manager as a service so other /// resource serializers can get at it. SerializationResourceManager /// has additional methods on it to support writing data /// into the resource streams for various cultures. /// internal class ResourceCodeDomSerializer : CodeDomSerializer { private static ResourceCodeDomSerializer defaultSerializer; ////// /// Retrieves a default static instance of this serializer. /// internal new static ResourceCodeDomSerializer Default { get { if (defaultSerializer == null) { defaultSerializer = new ResourceCodeDomSerializer(); } return defaultSerializer; } } public override string GetTargetComponentName(CodeStatement statement, CodeExpression expression, Type type) { string name = null; CodeExpressionStatement expStatement = statement as CodeExpressionStatement; if (expStatement != null) { CodeMethodInvokeExpression methodInvokeEx = expStatement.Expression as CodeMethodInvokeExpression; if (methodInvokeEx != null) { CodeMethodReferenceExpression methodReferenceEx = methodInvokeEx.Method as CodeMethodReferenceExpression; if (methodReferenceEx != null && string.Equals(methodReferenceEx.MethodName, "ApplyResources", StringComparison.OrdinalIgnoreCase) && methodInvokeEx.Parameters.Count > 0) { // We've found a call to the ApplyResources method on a ComponentResourceManager object. // now we just need to figure out which component ApplyResources is being called for, and // put it into that component's bucket. CodeFieldReferenceExpression fieldReferenceEx = methodInvokeEx.Parameters[0] as CodeFieldReferenceExpression; CodeVariableReferenceExpression variableReferenceEx = methodInvokeEx.Parameters[0] as CodeVariableReferenceExpression; if (fieldReferenceEx != null && fieldReferenceEx.TargetObject is CodeThisReferenceExpression) { name = fieldReferenceEx.FieldName; } else if (variableReferenceEx != null) { name = variableReferenceEx.VariableName; } } } } if (string.IsNullOrEmpty(name)) { name = base.GetTargetComponentName(statement, expression, type); } return name; } ////// /// This is the name of the resource manager object we declare /// on the component surface. /// private string ResourceManagerName { get { return "resources"; } } ////// /// Deserilizes the given CodeDom object into a real object. This /// will use the serialization manager to create objects and resolve /// data types. The root of the object graph is returned. /// public override object Deserialize(IDesignerSerializationManager manager, object codeObject) { object instance = null; if (manager == null || codeObject == null) { throw new ArgumentNullException(manager == null ? "manager" : "codeObject"); } using (TraceScope("ResourceCodeDomSerializer::Deserialize")) { // What is the code object? We support an expression, a statement or a collection of statements CodeExpression expression = codeObject as CodeExpression; if (expression != null) { instance = DeserializeExpression(manager, null, expression); } else { CodeStatementCollection statements = codeObject as CodeStatementCollection; if (statements != null) { foreach (CodeStatement element in statements) { // Do special parsing of the resources statement if (element is CodeVariableDeclarationStatement) { // We create the resource manager ouselves here because it's not just a straight // parse of the code. // CodeVariableDeclarationStatement statement = (CodeVariableDeclarationStatement)element; TraceWarningIf(!statement.Name.Equals(ResourceManagerName), "WARNING: Resource manager serializer being invoked to deserialize a collection we didn't create."); if (statement.Name.Equals(ResourceManagerName)) { instance = CreateResourceManager(manager); } } else { // If we do not yet have an instance, we will need to pick through the // statements and see if we can find one. if (instance == null) { instance = DeserializeStatementToInstance(manager, element); } else { DeserializeStatement(manager, element); } } } } else { CodeStatement statement = codeObject as CodeStatement; if (statement == null) { Debug.Fail("ResourceCodeDomSerializer::Deserialize requires a CodeExpression, CodeStatement or CodeStatementCollection to parse"); string supportedTypes = string.Format(CultureInfo.CurrentCulture, "{0}, {1}, {2}", typeof(CodeExpression).Name, typeof(CodeStatement).Name, typeof(CodeStatementCollection).Name); throw new ArgumentException(SR.GetString(SR.SerializerBadElementTypes, codeObject.GetType().Name, supportedTypes)); } } } } return instance; } private SerializationResourceManager CreateResourceManager(IDesignerSerializationManager manager) { Trace("Variable is our resource manager. Creating it"); SerializationResourceManager sm = GetResourceManager(manager); TraceWarningIf(sm.DeclarationAdded, "We have already created a resource manager."); if (!sm.DeclarationAdded) { sm.DeclarationAdded = true; manager.SetName(sm, ResourceManagerName); } return sm; } ////// This method is invoked during deserialization to obtain an instance of an object. When this is called, an instance /// of the requested type should be returned. Our implementation provides a design time resource manager. /// protected override object DeserializeInstance(IDesignerSerializationManager manager, Type type, object[] parameters, string name, bool addToContainer) { if (manager == null) throw new ArgumentNullException("manager"); if (type == null) throw new ArgumentNullException("type"); if (name != null && name.Equals(ResourceManagerName) && typeof(ResourceManager).IsAssignableFrom(type)) { return CreateResourceManager(manager); } else { // if it isn't our special resource manager, just create it. return manager.CreateInstance(type, parameters, name, addToContainer); } } ////// /// Deserilizes the given CodeDom object into a real object. This /// will use the serialization manager to create objects and resolve /// data types. It uses the invariant resource blob to obtain resources. /// public object DeserializeInvariant(IDesignerSerializationManager manager, string resourceName) { SerializationResourceManager resources = GetResourceManager(manager); return resources.GetObject(resourceName, true); } ////// Try to discover the data type we should apply a cast for. To do this, we /// first search the context stack for an ExpressionContext to decrypt, and if /// we fail that we try the actual object. If we can't find a cast type we /// return null. /// private Type GetCastType(IDesignerSerializationManager manager, object value) { // Is there an ExpressionContext we can work with? // ExpressionContext tree = (ExpressionContext)manager.Context[typeof(ExpressionContext)]; if (tree != null) { return tree.ExpressionType; } // Party on the object, if we can. It is the best identity we can get. // if (value != null) { Type castTo = value.GetType(); while (!castTo.IsPublic && !castTo.IsNestedPublic) { castTo = castTo.BaseType; } return castTo; } // Object is null. Nothing we can do // TraceError("We need to supply a cast, but we cannot determine the cast type."); return null; } ////// /// Retrieves a dictionary enumerator for the requested culture, or null if no resources for that culture exist. /// public IDictionaryEnumerator GetEnumerator(IDesignerSerializationManager manager, CultureInfo culture) { SerializationResourceManager resources = GetResourceManager(manager); return resources.GetEnumerator(culture); } ////// Retrieves a dictionary enumerator for the requested culture, or null if no resources for that culture exist. /// public IDictionaryEnumerator GetMetadataEnumerator(IDesignerSerializationManager manager) { SerializationResourceManager resources = GetResourceManager(manager); return resources.GetMetadataEnumerator(); } ////// Demand creates the serialization resource manager. Stores the manager as an appended context value. /// private SerializationResourceManager GetResourceManager(IDesignerSerializationManager manager) { SerializationResourceManager sm = manager.Context[typeof(SerializationResourceManager)] as SerializationResourceManager; if (sm == null) { sm = new SerializationResourceManager(manager); manager.Context.Append(sm); } return sm; } ////// /// Serializes the given object into a CodeDom object. This expects the following /// values to be available on the context stack: /// /// A CodeStatementCollection that we can add our resource declaration to, /// if necessary. /// /// An ExpressionContext that contains the property, field or method /// that is being serialized, along with the object being serialized. /// We need this so we can create a unique resource name for the /// object. /// /// public override object Serialize(IDesignerSerializationManager manager, object value) { return Serialize(manager, value, false, false, true); } ////// /// Serializes the given object into a CodeDom object. This expects the following /// values to be available on the context stack: /// /// A CodeStatementCollection that we can add our resource declaration to, /// if necessary. /// /// An ExpressionContext that contains the property, field or method /// that is being serialized, along with the object being serialized. /// We need this so we can create a unique resource name for the /// object. /// /// public object Serialize(IDesignerSerializationManager manager, object value, bool shouldSerializeInvariant) { return Serialize(manager, value, false, shouldSerializeInvariant, true); } ////// Serializes the given object into a CodeDom object. This expects the following /// values to be available on the context stack: /// /// A CodeStatementCollection that we can add our resource declaration to, /// if necessary. /// /// An ExpressionContext that contains the property, field or method /// that is being serialized, along with the object being serialized. /// We need this so we can create a unique resource name for the /// object. /// /// public object Serialize(IDesignerSerializationManager manager, object value, bool shouldSerializeInvariant, bool ensureInvariant) { return Serialize(manager, value, false, shouldSerializeInvariant, ensureInvariant); } ////// /// This performs the actual work of serialization between Serialize and SerializeInvariant. /// private object Serialize(IDesignerSerializationManager manager, object value, bool forceInvariant, bool shouldSerializeInvariant, bool ensureInvariant) { CodeExpression expression = null; using (TraceScope("ResourceCodeDomSerializer::Serialize")) { // Resource serialization is a little inconsistent. We deserialize our own resource manager // creation statement, but we will never be asked to serialize a resource manager, because // it doesn't exist as a product of the design container; it is purely an artifact of // serializing. Some not-so-obvious side effects of this are: // // This method will never ever be called by the serialization system directly. // There is no attribute or metadata that will invoke it. Instead, other // serializers will call this method to see if we should serialize to resources. // // We need a way to inject the local variable declaration into the method body // for the resource manager if we actually do emit a resource, which we shove // into the statements collection. SerializationResourceManager sm = GetResourceManager(manager); CodeStatementCollection statements = (CodeStatementCollection)manager.Context[typeof(CodeStatementCollection)]; // If this serialization resource manager has never been used to output // culture-sensitive statements, then we must emit the local variable hookup. Culture // invariant statements are used to save random data that is not representable in code, // so there is no need to emit a declaration. // if (!forceInvariant) { if (!sm.DeclarationAdded) { sm.DeclarationAdded = true; // If we have a root context, then we can write out a reasonable resource manager constructor. // If not, then we're a bit hobbled because we have to guess at the resource name. RootContext rootCxt = manager.Context[typeof(RootContext)] as RootContext; TraceWarningIf(statements == null, "No CodeStatementCollection on serialization stack, we cannot serialize resource manager creation statements."); if (statements != null) { CodeExpression[] parameters; if (rootCxt != null) { string baseType = manager.GetName(rootCxt.Value); parameters = new CodeExpression[] { new CodeTypeOfExpression(baseType) }; } else { TraceWarning("No root context, we can only assume the resource manager resource name."); parameters = new CodeExpression[] { new CodePrimitiveExpression(ResourceManagerName) }; } CodeExpression initExpression = new CodeObjectCreateExpression(typeof(ComponentResourceManager), parameters); statements.Add(new CodeVariableDeclarationStatement(typeof(ComponentResourceManager), ResourceManagerName, initExpression)); SetExpression(manager, sm, new CodeVariableReferenceExpression(ResourceManagerName)); sm.ExpressionAdded = true; ComponentCache cache = manager.Context[typeof(ComponentCache)] as ComponentCache; ComponentCache.Entry entry = manager.Context[typeof(ComponentCache.Entry)] as ComponentCache.Entry; } } else { // Check to see if we have an expression for SM yet. If we have cached the declaration // in the component cache, the expression may not be setup so we should re-apply it. if (!sm.ExpressionAdded) { if (GetExpression(manager, sm) == null) { SetExpression(manager, sm, new CodeVariableReferenceExpression(ResourceManagerName)); } sm.ExpressionAdded = true; } } } // Retrieve the ExpressionContext on the context stack, and save the value as a resource. ExpressionContext tree = (ExpressionContext)manager.Context[typeof(ExpressionContext)]; TraceWarningIf(tree == null, "No ExpressionContext on stack. We can serialize, but we cannot create a well-formed name."); string resourceName = sm.SetValue(manager, tree, value, forceInvariant, shouldSerializeInvariant, ensureInvariant, false); // Now the next step is to discover the type of the given value. If it is a string, // we will invoke "GetString" Otherwise, we will invoke "GetObject" and supply a // cast to the proper value. // bool needCast; string methodName; if (value is string || (tree != null && tree.ExpressionType == typeof(string))) { needCast = false; methodName = "GetString"; } else { needCast = true; methodName = "GetObject"; } // Finally, all we need to do is create a CodeExpression that represents the resource manager // method invoke. // Trace("Creating method invoke to {0}.{1}", ResourceManagerName, methodName); CodeMethodInvokeExpression methodInvoke = new CodeMethodInvokeExpression(); methodInvoke.Method = new CodeMethodReferenceExpression(new CodeVariableReferenceExpression(ResourceManagerName), methodName); methodInvoke.Parameters.Add(new CodePrimitiveExpression(resourceName)); if (needCast) { Type castTo = GetCastType(manager, value); if (castTo != null) { Trace("Supplying cast to {0}", castTo.Name); expression = new CodeCastExpression(castTo, methodInvoke); } else { expression = methodInvoke; } } else { expression = methodInvoke; } } return expression; } ////// /// Serializes the given object into a CodeDom object saving resources /// in the invariant culture, rather than the current culture. This expects the following /// values to be available on the context stack: /// /// A CodeStatementCollection that we can add our resource declaration to, /// if necessary. /// /// An ExpressionContext that contains the property, field or method /// that is being serialized, along with the object being serialized. /// We need this so we can create a unique resource name for the /// object. /// /// public object SerializeInvariant(IDesignerSerializationManager manager, object value, bool shouldSerializeValue) { return Serialize(manager, value, true, shouldSerializeValue, true); } ////// Writes out the given metadata. /// public void SerializeMetadata(IDesignerSerializationManager manager, string name, object value, bool shouldSerializeValue) { using (TraceScope("ResourceCodeDomSerializer::SerializeMetadata")) { Trace("Name: {0}", name); Trace("Value: {0}", (value == null ? "(null)" : value.ToString())); SerializationResourceManager sm = GetResourceManager(manager); sm.SetMetadata(manager, name, value, shouldSerializeValue, false); } } ////// /// Serializes the given resource value into the resource set. This does not effect /// the code dom values. The resource is written into the current culture. /// public void WriteResource(IDesignerSerializationManager manager, string name, object value) { using (TraceScope("ResourceCodeDomSerializer::WriteResource")) { Trace("Name: {0}", name); Trace("Value: {0}", (value == null ? "(null)" : value.ToString())); SerializationResourceManager sm = GetResourceManager(manager); sm.SetValue(manager, name, value, false, false, true, false); } } ////// /// Serializes the given resource value into the resource set. This does not effect /// the code dom values. The resource is written into the invariant culture. /// public void WriteResourceInvariant(IDesignerSerializationManager manager, string name, object value) { using (TraceScope("ResourceCodeDomSerializer::WriteResourceInvariant")) { Trace("Name: {0}", name); Trace("Value: {0}", (value == null ? "(null)" : value.ToString())); SerializationResourceManager sm = GetResourceManager(manager); sm.SetValue(manager, name, value, true, true, true, false); } } ////// This is called by the component code dom serializer's caching logic to save cached /// resource data back into the resx files. /// internal void ApplyCacheEntry(IDesignerSerializationManager manager, ComponentCache.Entry entry) { SerializationResourceManager sm = GetResourceManager(manager); if (entry.Metadata != null) { foreach(ComponentCache.ResourceEntry re in entry.Metadata) { sm.SetMetadata(manager, re.Name, re.Value, re.ShouldSerializeValue, true); } } if (entry.Resources != null) { foreach(ComponentCache.ResourceEntry re in entry.Resources) { manager.Context.Push(re.PropertyDescriptor); manager.Context.Push(re.ExpressionContext); try { sm.SetValue(manager, re.Name, re.Value, re.ForceInvariant, re.ShouldSerializeValue, re.EnsureInvariant, true); } finally { Debug.Assert(manager.Context.Current == re.ExpressionContext, "Someone corrupted the context stack"); manager.Context.Pop(); Debug.Assert(manager.Context.Current == re.PropertyDescriptor, "Someone corrupted the context stack"); manager.Context.Pop(); } } } } ////// /// This is the meat of resource serialization. This implements /// a resource manager through a host-provided IResourceService /// interface. The resource service feeds us with resource /// readers and writers, and we simulate a runtime ResourceManager. /// There is one instance of this object for the entire serialization /// process, just like there is one resource manager in runtime /// code. When an instance of this object is created, it /// adds itself to the serialization manager's service list, /// and listens for the SerializationComplete event. When /// serialization is complete, this will close and flush /// any readers or writers it may have opened and will /// also remove itself from the service list. /// private class SerializationResourceManager : ComponentResourceManager { private static object resourceSetSentinel = new object(); private IDesignerSerializationManager manager; private bool checkedLocalizationLanguage; private CultureInfo localizationLanguage; private IResourceWriter writer; private CultureInfo readCulture; private Hashtable nameTable; private Hashtable resourceSets; private Hashtable metadata; private Hashtable mergedMetadata; private object rootComponent; private bool declarationAdded = false; private bool expressionAdded = false; private Hashtable propertyFillAdded; private bool invariantCultureResourcesDirty = false; private bool metadataResourcesDirty = false; public SerializationResourceManager(IDesignerSerializationManager manager) { this.manager = manager; this.nameTable = new Hashtable(); // We need to know when we're done so we can push the resource file out. // manager.SerializationComplete += new EventHandler(OnSerializationComplete); } ////// /// State the serializers use to determine if the declaration /// of this resource manager has been performed. This is just /// per-document state we keep; we do not actually care about /// this value. /// public bool DeclarationAdded { get { return declarationAdded; } set { declarationAdded = value; } } ////// When a declaration is added, we also setup an expression other serializers /// can use to reference our resource declaration. This bit tracks if we /// have setup this expression yet. Note that the expression and declaration may /// be added at diffrerent times, if the declaration was added by a cached /// component. /// public bool ExpressionAdded { get { return expressionAdded; } set { expressionAdded = value; } } ////// /// The language we should be localizing into. /// private CultureInfo LocalizationLanguage { get { if (!checkedLocalizationLanguage) { // Check to see if our base component's localizable prop is true RootContext rootCxt = manager.Context[typeof(RootContext)] as RootContext; if (rootCxt != null) { object comp = rootCxt.Value; PropertyDescriptor prop = TypeDescriptor.GetProperties(comp)["LoadLanguage"]; if (prop != null && prop.PropertyType == typeof(CultureInfo)) { localizationLanguage = (CultureInfo)prop.GetValue(comp); } } checkedLocalizationLanguage = true; } return localizationLanguage; } } ////// /// This is the culture info we should use to read and write resources. We always write /// using the same culture we read with so we don't stomp on data. /// private CultureInfo ReadCulture { get { if (readCulture == null) { CultureInfo locCulture = LocalizationLanguage; if (locCulture != null) { readCulture = locCulture; } else { readCulture = CultureInfo.InvariantCulture; } } return readCulture; } } ////// /// Returns a hash table where we shove resource sets. /// private Hashtable ResourceTable { get { if (resourceSets == null) { resourceSets = new Hashtable(); } return resourceSets; } } ////// /// Retrieves the root component we're designing. /// private object RootComponent { get { if (rootComponent == null) { RootContext rootCxt = manager.Context[typeof(RootContext)] as RootContext; if (rootCxt != null) { rootComponent = rootCxt.Value; } } return rootComponent; } } ////// /// Retrieves a resource writer we should write into. /// private IResourceWriter Writer { get { if (writer == null) { IResourceService rs = (IResourceService)manager.GetService(typeof(IResourceService)); if (rs != null) { // We always write with the culture we read with. In the event of a language change // during localization, we will write the new language to the source code and then // perform a reload. // writer = rs.GetResourceWriter(ReadCulture); } else { // No resource service, so there is no way to create a resource writer for the // object. In this case we just create an empty one so the resources go into // the bit-bucket. // Debug.Fail("We expected to get IResourceService -- no resource serialization will be available"); writer = new ResourceWriter(new MemoryStream()); } } return writer; } } ////// The component serializer supports caching serialized outputs for speed. It holds both a collection of statements as well /// as an opaque blob for resources. This function adds data to that blob. The parameters to this function are the /// same as those to SetValue, or SetMetadata (when isMetadata is true). /// private void AddCacheEntry(IDesignerSerializationManager manager, string name, object value, bool isMetadata, bool forceInvariant, bool shouldSerializeValue, bool ensureInvariant) { ComponentCache.Entry entry = manager.Context[typeof(ComponentCache.Entry)] as ComponentCache.Entry; if (entry != null) { ComponentCache.ResourceEntry re = new ComponentCache.ResourceEntry(); re.Name = name; re.Value = value; re.ForceInvariant = forceInvariant; re.ShouldSerializeValue = shouldSerializeValue; re.EnsureInvariant = ensureInvariant; re.PropertyDescriptor = (PropertyDescriptor)manager.Context[typeof(PropertyDescriptor)]; re.ExpressionContext = (ExpressionContext)manager.Context[typeof(ExpressionContext)]; if (isMetadata) { entry.AddMetadata(re); } else { entry.AddResource(re); } } } ////// Returns true if the caller should add a property fill statement /// for the given object. A property fill is required for the /// component only once, so this remembers the value. /// public bool AddPropertyFill(object value) { bool added = false; if (propertyFillAdded == null) { propertyFillAdded = new Hashtable(); } else { added = propertyFillAdded.ContainsKey(value); } if (!added) { propertyFillAdded[value] = value; } return !added; } ////// This method examines all the resources for the provided culture. /// When it finds a resource with a key in the format of /// "[objectName].[property name]" it will apply that resources value /// to the corresponding property on the object. /// public override void ApplyResources(object value, string objectName, CultureInfo culture) { if (culture == null) { culture = ReadCulture; } base.ApplyResources(value, objectName, culture); } ////// /// This determines if the given resource name/value pair can be retrieved /// from a parent culture. We don't want to write duplicate resources for /// each language, so we do a check of the parent culture. /// private CompareValue CompareWithParentValue(string name, object value) { Debug.Assert(name != null, "name is null"); // If there is no parent culture, treat that as being different from the parent's resource, // which results in the "normal" code path for the caller. if (ReadCulture.Equals(CultureInfo.InvariantCulture)) return CompareValue.Different; CultureInfo culture = ReadCulture; for (;;) { Debug.Assert(culture.Parent != culture, "should have exited loop when culture = InvariantCulture"); culture = culture.Parent; Hashtable rs = GetResourceSet(culture); bool contains = (rs == null) ? false : rs.ContainsKey(name); if (contains) { object parentValue = (rs != null) ? rs[name] : null; if (parentValue == value) { return CompareValue.Same; } else if (parentValue != null) { if (parentValue.Equals(value)) return CompareValue.Same; else return CompareValue.Different; } else { return CompareValue.Different; } } else if (culture.Equals(CultureInfo.InvariantCulture)) { return CompareValue.New; } } } ////// /// Creates a resource set hashtable for the given resource /// reader. /// [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2102:CatchNonClsCompliantExceptionsInGeneralHandlers")] private Hashtable CreateResourceSet(IResourceReader reader, CultureInfo culture) { Hashtable result = new Hashtable(); // We need to guard against bad or unloadable resources. We warn the user in the task // list here, but we will still load the designer. // try { IDictionaryEnumerator resEnum = reader.GetEnumerator(); while (resEnum.MoveNext()) { string name = (string)resEnum.Key; object value = resEnum.Value; result[name] = value; } } catch (Exception e) { string message = e.Message; if (message == null || message.Length == 0) { message = e.GetType().Name; } Exception se; if (culture == CultureInfo.InvariantCulture) { se = new SerializationException(SR.GetString(SR.SerializerResourceExceptionInvariant, message), e); } else { se = new SerializationException(SR.GetString(SR.SerializerResourceException, culture.ToString(), message), e); } manager.ReportError(se); } return result; } ////// This returns a dictionary enumerator for metadata on the invariant culture. If no metadata /// can be found this will return null.. /// public IDictionaryEnumerator GetMetadataEnumerator() { if (mergedMetadata == null) { Hashtable t = GetMetadata(); if (t != null) { // This is for backwards compatibility and also for the case when our reader/writer // don't support metadata. We must merge the original enumeration data in here or // else existing design time properties won't show up. That would be really // bad for things like Localizable. Hashtable it = GetResourceSet(CultureInfo.InvariantCulture); if (it != null) { foreach(DictionaryEntry de in it) { if (!t.ContainsKey(de.Key)) { t.Add(de.Key, de.Value); } } } mergedMetadata = t; } } if (mergedMetadata != null) { return mergedMetadata.GetEnumerator(); } return null; } ////// /// This returns a dictionary enumerator for the given culture. If no such resource file exists for the culture this /// will return null. /// public IDictionaryEnumerator GetEnumerator(CultureInfo culture) { Hashtable ht = GetResourceSet(culture); if (ht != null) { return ht.GetEnumerator(); } return null; } ////// Loads the metadata table /// private Hashtable GetMetadata() { if (metadata == null) { IResourceService resSvc = (IResourceService)manager.GetService(typeof(IResourceService)); if (resSvc != null) { IResourceReader reader = resSvc.GetResourceReader(CultureInfo.InvariantCulture); if (reader != null) { try { ResXResourceReader resxReader = reader as ResXResourceReader; if (resxReader != null) { metadata = new Hashtable(); IDictionaryEnumerator de = resxReader.GetMetadataEnumerator(); while (de.MoveNext()) { metadata[de.Key] = de.Value; } } } finally { reader.Close(); } } } } return metadata; } ////// /// Overrides ResourceManager.GetObject to return the requested /// object. Returns null if the object couldn't be found. /// public override object GetObject(string resourceName) { return GetObject(resourceName, false); } ////// /// Retrieves the object of the given name from our resource bundle. /// If forceInvariant is true, this will always use the invariant /// resource, rather than using the current language. /// Returns null if the object couldn't be found. /// public object GetObject(string resourceName, bool forceInvariant) { Debug.Assert(manager != null, "This resource manager object has been destroyed."); // We fetch the read culture if someone asks for a // culture-sensitive string. If forceInvariant is set, we always // use the invariant culture. // CultureInfo culture; if (forceInvariant) { culture = CultureInfo.InvariantCulture; } else { culture = ReadCulture; } object value = null; while (value == null) { Hashtable rs = GetResourceSet(culture); if (rs != null) { value = rs[resourceName]; } CultureInfo lastCulture = culture; culture = culture.Parent; if (lastCulture.Equals(culture)) { break; } } return value; } ////// /// Looks up the resource set in the resourceSets hash table, loading the set if it hasn't been loaded already. /// Returns null if no resource that exists for that culture. /// private Hashtable GetResourceSet(CultureInfo culture) { Debug.Assert(culture != null, "null parameter"); Hashtable rs = null; object objRs = ResourceTable[culture]; if (objRs == null) { IResourceService resSvc = (IResourceService)manager.GetService(typeof(IResourceService)); TraceErrorIf(resSvc == null, "IResourceService is not available. We will not be able to load resources."); if (resSvc != null) { IResourceReader reader = resSvc.GetResourceReader(culture); if (reader != null) { try { rs = CreateResourceSet(reader, culture); } finally { reader.Close(); } ResourceTable[culture] = rs; } else { // Provide a sentinel so we don't repeatedly ask // for the same resource. If this is the invariant // culture, always provide one. // if (culture.Equals(CultureInfo.InvariantCulture)) { rs = new Hashtable(); ResourceTable[culture] = rs; } else { ResourceTable[culture] = resourceSetSentinel; } } } } else { rs = objRs as Hashtable; if (rs == null) { // the resourceSets hash table may contain our "this" pointer as a sentinel value Debug.Assert(objRs == resourceSetSentinel, "unknown object in resourceSets: " + objRs); } } return rs; } ////// Override of GetResourceSet from ResourceManager. /// public override ResourceSet GetResourceSet(CultureInfo culture, bool createIfNotExists, bool tryParents) { if (culture == null) { throw new ArgumentNullException("culture"); } CultureInfo lastCulture = culture; do { Hashtable ht = GetResourceSet(culture); if (ht != null) { return new CodeDomResourceSet(ht); } lastCulture = culture; culture = culture.Parent; } while (tryParents && !lastCulture.Equals(culture)); if (createIfNotExists) { return new CodeDomResourceSet(); } return null; } ////// /// Overrides ResourceManager.GetString to return the requested /// string. Returns null if the string couldn't be found. /// public override string GetString(string resourceName) { return GetObject(resourceName, false) as string; } ////// /// Event handler that gets called when serialization or deserialization /// is complete. Here we need to write any resources to disk. Sine /// we open resources for write on demand, this code handles the case /// of reading resources as well. /// private void OnSerializationComplete(object sender, EventArgs e) { // Commit any changes we have made. // if (writer != null) { writer.Close(); writer = null; } if (invariantCultureResourcesDirty || metadataResourcesDirty) { IResourceService service = (IResourceService)manager.GetService(typeof(IResourceService)); if (service != null) { IResourceWriter invariantWriter = service.GetResourceWriter(CultureInfo.InvariantCulture); Debug.Assert(invariantWriter != null, "GetResourceWriter returned null for the InvariantCulture"); try { // Do the invariant resources first Debug.Assert(!ReadCulture.Equals(CultureInfo.InvariantCulture), "invariantCultureResourcesDirty should only come into play when readCulture != CultureInfo.InvariantCulture; check that CompareWithParentValue is correct"); object objRs = ResourceTable[CultureInfo.InvariantCulture]; Debug.Assert(objRs != null && objRs is Hashtable, "ResourceSet for the InvariantCulture not loaded, but it's considered dirty?"); Hashtable resourceSet = (Hashtable)objRs; // Dump the hash table to the resource writer // IDictionaryEnumerator resEnum = resourceSet.GetEnumerator(); while (resEnum.MoveNext()) { string name = (string)resEnum.Key; object value = resEnum.Value; invariantWriter.AddResource(name, value); } invariantCultureResourcesDirty = false; // Followed by the metadata. Debug.Assert(metadata != null, "No metadata, but it's dirty?"); ResXResourceWriter resxWriter = invariantWriter as ResXResourceWriter; if (resxWriter != null) { foreach (DictionaryEntry de in metadata) { resxWriter.AddMetadata((string)de.Key, de.Value); } } else { Debug.Fail("Metadata not supported, but it's dirty?"); } metadataResourcesDirty = false; } finally { invariantWriter.Close(); } } else { Debug.Fail("Couldn't find IResourceService"); invariantCultureResourcesDirty = false; metadataResourcesDirty = false; } } } ////// Writes a metadata tag to the resource, or writes a normal /// tag if the resource writer doesn't support metadata. /// public void SetMetadata(IDesignerSerializationManager manager, string resourceName, object value, bool shouldSerializeValue, bool applyingCachedResources) { if (value != null && (!value.GetType().IsSerializable)) { Debug.Fail("Cannot save a non-serializable value into resources. Add serializable to " + (value == null ? "(null)" : value.GetType().Name)); return; } // If we are currently the invariant culture then we may be able to // write directly. if (ReadCulture.Equals(CultureInfo.InvariantCulture)) { ResXResourceWriter resxWriter = Writer as ResXResourceWriter; if (shouldSerializeValue) { if (resxWriter != null) { resxWriter.AddMetadata(resourceName, value); } else { Writer.AddResource(resourceName, value); } } } else { Hashtable t = null; // Check if the invariant writer supports metadata. If not, we need to push metadata // as regular data. IResourceWriter invariantWriter = null; IResourceService service = (IResourceService)manager.GetService(typeof(IResourceService)); if (service != null) { invariantWriter = service.GetResourceWriter(CultureInfo.InvariantCulture); } Hashtable invariant = GetResourceSet(CultureInfo.InvariantCulture); if (invariantWriter == null || invariantWriter is ResXResourceWriter) { t = GetMetadata(); if (t == null) { metadata = new Hashtable(); t = metadata; } // Note that when we read metadata, for backwards compatibility, we also merge in regular data // from the invariant resource. We need to clear that data here, since we are going to write // out metadata separately. if (invariant.ContainsKey(resourceName)) { invariant.Remove(resourceName); } metadataResourcesDirty = true; } else { t = invariant; invariantCultureResourcesDirty = true; } Debug.Assert(t != null, "Don't know where to push metadata."); if (t != null) { if (shouldSerializeValue) { t[resourceName] = value; } else { t.Remove(resourceName); } } mergedMetadata = null; } // Update the component cache, if we have one active if (!applyingCachedResources) { AddCacheEntry(manager, resourceName, value, true, false, shouldSerializeValue, false); } } ////// /// Writes the given resource value under the given name. /// This checks the parent resource to see if the values are the /// same. If they are, the resource is not written. If not, then /// the resource is written. We always write using the resource language /// we read in with, so we don't stomp on the wrong resource data in the /// event that someone changes the language. /// public void SetValue(IDesignerSerializationManager manager, string resourceName, object value, bool forceInvariant, bool shouldSerializeInvariant, bool ensureInvariant, bool applyingCachedResources) { // Values we are going to serialize must be serializable or else // the resource writer will fail when we close it. // if (value != null && (!value.GetType().IsSerializable)) { Debug.Fail("Cannot save a non-serializable value into resources. Add serializable to " + (value == null ? "(null)" : value.GetType().Name)); return; } if (forceInvariant) { if (ReadCulture.Equals(CultureInfo.InvariantCulture)) { if (shouldSerializeInvariant) { Writer.AddResource(resourceName, value); } } else { Hashtable resourceSet = GetResourceSet(CultureInfo.InvariantCulture); Debug.Assert(resourceSet != null, "No ResourceSet for the InvariantCulture?"); if (shouldSerializeInvariant) { resourceSet[resourceName] = value; } else { resourceSet.Remove(resourceName); } invariantCultureResourcesDirty = true; } } else { CompareValue comparison = CompareWithParentValue(resourceName, value); switch (comparison) { case CompareValue.Same: // don't add to any resource set break; case CompareValue.Different: Writer.AddResource(resourceName, value); break; case CompareValue.New: if (ensureInvariant) { // Add resource to InvariantCulture Debug.Assert(!ReadCulture.Equals(CultureInfo.InvariantCulture), "invariantCultureResourcesDirty should only come into play when readCulture != CultureInfo.InvariantCulture; check that CompareWithParentValue is correct"); Hashtable resourceSet = GetResourceSet(CultureInfo.InvariantCulture); Debug.Assert(resourceSet != null, "No ResourceSet for the InvariantCulture?"); resourceSet[resourceName] = value; invariantCultureResourcesDirty = true; Writer.AddResource(resourceName, value); } else { // This is a new value. We want to write it out, PROVIDED // that the value is not associated with a property that is currently // returning false from ShouldSerializeValue. This allows us to skip writing out // Font == NULL on all non-invariant cultures, but still allow us to // write out the value if the user is resetting a font back to null. // If we cannot associate the value with a property we will write // it out just to be safe. // // In addition, we need to handle the case of the user adding a new // component to the non-invariant language. This would be bad, because // when he/she moved back to the invariant language the component's properties // would all be defaults. In order to minimize this problem, but still allow // holes in the invariant resx, we also check to see if the property can // be reset. If it cannot be reset, that means that it has no meaningful // default. Therefore, it should have appeared in the invariant resx and its // absence indicates a new component. // bool writeValue = true; bool writeInvariant = false; PropertyDescriptor prop = (PropertyDescriptor)manager.Context[typeof(PropertyDescriptor)]; if (prop != null) { ExpressionContext tree = (ExpressionContext)manager.Context[typeof(ExpressionContext)]; if (tree != null && tree.Expression is CodePropertyReferenceExpression) { writeValue = prop.ShouldSerializeValue(tree.Owner); writeInvariant = !prop.CanResetValue(tree.Owner); } } if (writeValue) { Writer.AddResource(resourceName, value); if (writeInvariant) { // Add resource to InvariantCulture Debug.Assert(!ReadCulture.Equals(CultureInfo.InvariantCulture), "invariantCultureResourcesDirty should only come into play when readCulture != CultureInfo.InvariantCulture; check that CompareWithParentValue is correct"); Hashtable resourceSet = GetResourceSet(CultureInfo.InvariantCulture); Debug.Assert(resourceSet != null, "No ResourceSet for the InvariantCulture?"); resourceSet[resourceName] = value; invariantCultureResourcesDirty = true; } } } break; default: Debug.Fail("Unknown CompareValue " + comparison); break; } } // Update the component cache, if we have one active. We don't have to be fancy here // because updating this cache just indicates that code in the component cache will later // call us to re-apply the resources, and our logic above will be called again. if (!applyingCachedResources) { AddCacheEntry(manager, resourceName, value, false, forceInvariant, shouldSerializeInvariant, ensureInvariant); } } ////// /// Writes the given resource value under the given name. /// This checks the parent resource to see if the values are the /// same. If they are, the resource is not written. If not, then /// the resource is written. We always write using the resource language /// we read in with, so we don't stomp on the wrong resource data in the /// event that someone changes the language. /// public string SetValue(IDesignerSerializationManager manager, ExpressionContext tree, object value, bool forceInvariant, bool shouldSerializeInvariant, bool ensureInvariant, bool applyingCachedResources) { string nameBase = null; bool appendCount = false; if (tree != null) { if (tree.Owner == RootComponent) { nameBase = "$this"; } else { nameBase = manager.GetName(tree.Owner); if (nameBase == null) { IReferenceService referenceService = (IReferenceService)manager.GetService(typeof(IReferenceService)); if (referenceService != null) { nameBase = referenceService.GetName(tree.Owner); } } } CodeExpression expression = tree.Expression; string expressionName; if (expression is CodePropertyReferenceExpression) { expressionName = ((CodePropertyReferenceExpression)expression).PropertyName; } else if (expression is CodeFieldReferenceExpression) { expressionName = ((CodeFieldReferenceExpression)expression).FieldName; } else if (expression is CodeMethodReferenceExpression) { expressionName = ((CodeMethodReferenceExpression)expression).MethodName; if (expressionName.StartsWith("Set")) { expressionName = expressionName.Substring(3); } } else { expressionName = null; } if (nameBase == null) { nameBase = "resource"; } if (expressionName != null) { nameBase += "." + expressionName; } } else { nameBase = "resource"; appendCount = true; } // Now find an unused name // string resourceName = nameBase; int count = 1; for(;;) { if (appendCount) { resourceName = nameBase + count.ToString(CultureInfo.InvariantCulture); count++; } else { appendCount = true; } if (!nameTable.ContainsKey(resourceName)) { break; } } // Now that we have a name, write out the resource. // SetValue(manager, resourceName, value, forceInvariant, shouldSerializeInvariant, ensureInvariant, applyingCachedResources); nameTable[resourceName] = resourceName; return resourceName; } private class CodeDomResourceSet : ResourceSet { public CodeDomResourceSet() { } public CodeDomResourceSet(Hashtable resources) { Table = resources; } } private enum CompareValue { Same, // parent value == child value Different, // parent value exists, but != child value New, // parent value does not exist } } } } // 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
- SqlTriggerAttribute.cs
- ProgressBar.cs
- ManagementObjectCollection.cs
- RequiredFieldValidator.cs
- AnonymousIdentificationModule.cs
- DependencyStoreSurrogate.cs
- ResXFileRef.cs
- HtmlContainerControl.cs
- FixedSOMTable.cs
- XmlAttributeProperties.cs
- DataGridCellAutomationPeer.cs
- OrderedDictionaryStateHelper.cs
- AsyncInvokeContext.cs
- AuthorizationRuleCollection.cs
- MailDefinition.cs
- InternalPolicyElement.cs
- DataBindingExpressionBuilder.cs
- ChangePassword.cs
- IndentedTextWriter.cs
- ExtendedPropertyDescriptor.cs
- TextUtf8RawTextWriter.cs
- CapabilitiesPattern.cs
- RijndaelManaged.cs
- ConfigurationSettings.cs
- ActivityBuilderXamlWriter.cs
- ScopelessEnumAttribute.cs
- ISSmlParser.cs
- XmlArrayItemAttribute.cs
- BulletedListEventArgs.cs
- ScrollPattern.cs
- AssociationSetMetadata.cs
- TreeView.cs
- ToolStripDropDownClosedEventArgs.cs
- HtmlFormWrapper.cs
- ListViewVirtualItemsSelectionRangeChangedEvent.cs
- DataGridSortCommandEventArgs.cs
- _Connection.cs
- Int16.cs
- PageParserFilter.cs
- PropertyChangingEventArgs.cs
- CodeExpressionStatement.cs
- DataSetUtil.cs
- TreeNodeCollection.cs
- HttpValueCollection.cs
- XmlDataProvider.cs
- DataTable.cs
- CreateParams.cs
- RestHandler.cs
- OleDbInfoMessageEvent.cs
- SimpleType.cs
- SwitchCase.cs
- XmlByteStreamWriter.cs
- log.cs
- AuthStoreRoleProvider.cs
- MetadataSerializer.cs
- OleDbDataAdapter.cs
- IPipelineRuntime.cs
- MemberRelationshipService.cs
- SafeProcessHandle.cs
- _AutoWebProxyScriptEngine.cs
- Point4DValueSerializer.cs
- BooleanStorage.cs
- SelectionProviderWrapper.cs
- EntityConnectionStringBuilder.cs
- SimpleExpression.cs
- BaseDataList.cs
- RelationshipType.cs
- IdnMapping.cs
- AnimationTimeline.cs
- ValidatingPropertiesEventArgs.cs
- DBPropSet.cs
- ExpandedWrapper.cs
- WindowsContainer.cs
- PrintPreviewGraphics.cs
- DataSourceHelper.cs
- MemoryStream.cs
- XsdDuration.cs
- VariableAction.cs
- CFGGrammar.cs
- TabControl.cs
- XPathItem.cs
- DataGridViewRowsRemovedEventArgs.cs
- PartialTrustVisibleAssembly.cs
- EpmContentSerializerBase.cs
- PriorityQueue.cs
- EdmConstants.cs
- IISMapPath.cs
- ValueOfAction.cs
- TextReader.cs
- ConfigXmlComment.cs
- EventLogInformation.cs
- X509Certificate2.cs
- BuildDependencySet.cs
- SelectionHighlightInfo.cs
- KnownTypeAttribute.cs
- XmlElement.cs
- BindingFormattingDialog.cs
- InstanceHandleConflictException.cs
- StatusStrip.cs
- SchemaNamespaceManager.cs