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
- OpenFileDialog.cs
- _SslStream.cs
- ImmutableAssemblyCacheEntry.cs
- ColorAnimation.cs
- StructuralObject.cs
- TreeIterator.cs
- StringDictionaryCodeDomSerializer.cs
- WebPartConnectionsConnectVerb.cs
- TimerElapsedEvenArgs.cs
- EarlyBoundInfo.cs
- Transform3DGroup.cs
- WindowsComboBox.cs
- InitializationEventAttribute.cs
- CellIdBoolean.cs
- HttpContext.cs
- WizardStepBase.cs
- TableAdapterManagerMethodGenerator.cs
- ConfigurationStrings.cs
- QueryableDataSourceHelper.cs
- CustomAssemblyResolver.cs
- HiddenFieldPageStatePersister.cs
- ValidationSummary.cs
- BaseAsyncResult.cs
- IriParsingElement.cs
- EndpointAddressElementBase.cs
- WebPartMinimizeVerb.cs
- AdPostCacheSubstitution.cs
- CTreeGenerator.cs
- SqlNodeAnnotations.cs
- RectValueSerializer.cs
- BamlLocalizer.cs
- MobileRedirect.cs
- FixedElement.cs
- BStrWrapper.cs
- SectionUpdates.cs
- GenericUI.cs
- BookmarkList.cs
- StyleCollection.cs
- ThreadStaticAttribute.cs
- HttpCookieCollection.cs
- InputEventArgs.cs
- EventLogInformation.cs
- Blend.cs
- TraceListeners.cs
- SourceFileInfo.cs
- ArgumentDesigner.xaml.cs
- XmlAttribute.cs
- HttpCapabilitiesBase.cs
- KnownColorTable.cs
- CacheRequest.cs
- TreeViewItemAutomationPeer.cs
- ScaleTransform3D.cs
- OdbcHandle.cs
- ADRoleFactory.cs
- BordersPage.cs
- LinqDataSourceContextEventArgs.cs
- HashHelpers.cs
- Math.cs
- LinkDescriptor.cs
- Queue.cs
- ComponentEvent.cs
- SponsorHelper.cs
- FormViewInsertedEventArgs.cs
- Exception.cs
- AssemblyAttributesGoHere.cs
- Table.cs
- EntityViewGenerator.cs
- TreeIterator.cs
- RegistryPermission.cs
- returneventsaver.cs
- XmlSubtreeReader.cs
- _ConnectOverlappedAsyncResult.cs
- HostedHttpContext.cs
- DetailsViewCommandEventArgs.cs
- HtmlTableRowCollection.cs
- RegisteredDisposeScript.cs
- TdsParserStateObject.cs
- CacheOutputQuery.cs
- Encoder.cs
- Form.cs
- UserControlBuildProvider.cs
- LicenseProviderAttribute.cs
- Tile.cs
- ListItemDetailViewAttribute.cs
- BufferedReadStream.cs
- NativeRecognizer.cs
- SByteConverter.cs
- SemanticValue.cs
- SvcMapFileLoader.cs
- TraceSection.cs
- RecoverInstanceLocksCommand.cs
- LinqDataSourceUpdateEventArgs.cs
- CellParaClient.cs
- ToolStripContentPanelRenderEventArgs.cs
- SQLUtility.cs
- DocumentXPathNavigator.cs
- SoapCodeExporter.cs
- PieceNameHelper.cs
- XmlIncludeAttribute.cs
- PipelineModuleStepContainer.cs