pascalabcnet/VisualPascalABCNETLinux/SSYY/SampleDesignerSerializationManager.cs

570 lines
18 KiB
C#
Raw Normal View History

//------------------------------------------------------------------------------
/// <copyright from='1997' to='2002' company='Microsoft Corporation'>
/// Copyright (c) Microsoft Corporation. All Rights Reserved.
///
/// This source code is intended only as a supplement to Microsoft
/// Development Tools and/or on-line documentation. See these other
/// materials for detailed information regarding Microsoft code samples.
///
/// </copyright>
//------------------------------------------------------------------------------
using System;
using System.Collections;
using System.ComponentModel.Design.Serialization;
using System.ComponentModel;
using System.Reflection;
using System.Text;
using System.Diagnostics;
namespace SampleDesignerHost
{
/// Our implementation of IDesignerSerializationManager. This is a private
/// implementation and we only have one. So, it could have been implemented
/// as a private interface implementation on top of the loader class
/// itself. I decided against this because there is a lot of
/// state associated with the serialization manager and it could get
/// confusing.
internal class SampleDesignerSerializationManager : IDesignerSerializationManager
{
private SampleDesignerLoader _loader;
private ResolveNameEventHandler _resolveNameEventHandler;
private EventHandler _serializationCompleteEventHandler;
private ArrayList _designerSerializationProviders;
private Hashtable _instancesByName;
private Hashtable _namesByInstance;
private Hashtable _serializers;
private ArrayList _errorList;
private ContextStack _contextStack;
private PropertyDescriptorCollection _propertyCollection;
/// Initializes the serialization manager.
internal SampleDesignerSerializationManager(SampleDesignerLoader loader)
{
_loader = loader;
}
/// This method is called by dependent loads to add additional
/// errors to the error list. It is not called for the last
/// dependent load, nor is it called if IDesignerLoaderService
/// is not implemented. If outside parties want to implement
/// IDesignerLoaderService, they may, but they will have
/// to provide their own storage for the dependent
/// error list. We are just re-using _errorList here to be
/// efficient.
internal void AddErrors(ICollection errors)
{
if (errors != null && errors.Count > 0)
{
if (_errorList == null)
{
_errorList = new ArrayList();
}
_errorList.AddRange(errors);
}
}
/// This starts the loading process. Normally, everything
/// should be cleared out when this is called.
internal void Initialize()
{
if (_errorList != null)
{
_errorList.Clear();
}
}
/// This ends the loading process. This resets the state
/// of the serialization manager and merges the provided
/// error collection into the manager's own error list and
/// returns the merged list.
internal ICollection Terminate(ICollection errors)
{
// Let interested parties know that we're finished.
//
try
{
if (_serializationCompleteEventHandler != null)
{
_serializationCompleteEventHandler(this, EventArgs.Empty);
}
}
catch {}
// Merge the error list
//
if (_errorList != null && _errorList.Count > 0)
{
if (errors != null && errors.Count > 0)
{
_errorList.AddRange(errors);
}
errors = _errorList;
}
// Now disolve our state. The serialization manager
// should remain stateless.
//
_resolveNameEventHandler = null;
_serializationCompleteEventHandler = null;
_instancesByName = null;
_namesByInstance = null;
_serializers = null;
_errorList = null;
_contextStack = null;
return errors;
}
/// The Context property provides a user-defined storage area
/// implemented as a stack. This storage area is a useful way
/// to provide communication across serializers, as serialization
/// is a generally hierarchial process.
ContextStack IDesignerSerializationManager.Context
{
get
{
if (_contextStack == null)
{
_contextStack = new ContextStack();
}
return _contextStack;
}
}
/// The Properties property provides a set of custom properties
/// the serialization manager may surface. The set of properties
/// exposed here is defined by the implementor of
/// IDesignerSerializationManager.
PropertyDescriptorCollection IDesignerSerializationManager.Properties
{
get
{
if (_propertyCollection == null)
{
_propertyCollection = new PropertyDescriptorCollection(new PropertyDescriptor[] {});
}
return _propertyCollection;
}
}
/// ResolveName event. This event
/// is raised when GetName is called, but the name is not found
/// in the serialization manager's name table. It provides a
/// way for a serializer to demand-create an object so the serializer
/// does not have to order object creation by dependency. This
/// delegate is cleared immediately after serialization or deserialization
/// is complete.
event ResolveNameEventHandler IDesignerSerializationManager.ResolveName
{
add
{
_resolveNameEventHandler += value;
}
remove
{
_resolveNameEventHandler -= value;
}
}
/// This event is raised when serialization or deserialization
/// has been completed. Generally, serialization code should
/// be written to be stateless. Should some sort of state
/// be necessary to maintain, a serializer can listen to
/// this event to know when that state should be cleared.
/// An example of this is if a serializer needs to write
/// to another file, such as a resource file. In this case
/// it would be inefficient to design the serializer
/// to close the file when finished because serialization of
/// an object graph generally requires several _serializers.
/// The resource file would be opened and closed many times.
/// Instead, the resource file could be accessed through
/// an object that listened to the SerializationComplete
/// event, and that object could close the resource file
/// at the end of serialization.
event EventHandler IDesignerSerializationManager.SerializationComplete
{
add
{
_serializationCompleteEventHandler += value;
}
remove
{
_serializationCompleteEventHandler -= value;
}
}
/// This method adds a custom serialization provider to the
/// serialization manager. A custom serialization provider will
/// get the opportunity to return a serializer for a data type
/// before the serialization manager looks in the type's
/// metadata.
void IDesignerSerializationManager.AddSerializationProvider(IDesignerSerializationProvider provider)
{
if (_designerSerializationProviders == null)
{
_designerSerializationProviders = new ArrayList();
}
_designerSerializationProviders.Add(provider);
}
/// Creates an instance of the given type and adds it to a collection
/// of named instances. Objects that implement IComponent will be
/// added to the design time container if addToContainer is true.
object IDesignerSerializationManager.CreateInstance(Type type, ICollection arguments, string name, bool addToContainer)
{
object[] argArray = null;
if (arguments != null && arguments.Count > 0)
{
argArray = new object[arguments.Count];
arguments.CopyTo(argArray, 0);
}
object instance = null;
// We do some special casing here. If we are adding to the container, and if this type
// is an IComponent, then we don't create the instance through Activator, we go
// through the loader host. The reason for this is that if we went through activator,
// and if the object already specified a constructor that took an IContainer, our
// deserialization mechanism would equate the container to the designer host. This
// is the correct thing to do, but it has the side effect of adding the component
// to the designer host twice -- once with a default name, and a second time with
// the name we provide. This equates to a component rename, which isn't cheap,
// so we don't want to do it when we load each and every component.
//
if (addToContainer && typeof(IComponent).IsAssignableFrom(type))
{
IDesignerLoaderHost host = _loader.LoaderHost;
if (host != null)
{
if (name == null)
{
instance = host.CreateComponent(type);
}
else
{
instance = host.CreateComponent(type, name);
}
}
}
if (instance == null)
{
// If we have a name but the object wasn't a component
// that was added to the design container, save the
// name/value relationship in our own nametable.
//
if (name != null && _instancesByName != null)
{
if (_instancesByName.ContainsKey(name))
{
Exception ex = new InvalidOperationException("Duplicate component declaration for " + name + ".");
throw ex;
}
}
try
{
instance = Activator.CreateInstance(type, BindingFlags.Instance | BindingFlags.Public | BindingFlags.CreateInstance, null, argArray, null);
}
catch(MissingMethodException)
{
StringBuilder argTypes = new StringBuilder();
foreach (object o in argArray)
{
if (argTypes.Length > 0)
{
argTypes.Append(", ");
}
if (o != null)
{
argTypes.Append(o.GetType().Name);
}
else
{
argTypes.Append("null");
}
}
Exception ex = new InvalidOperationException("No matching constructor for " + type.FullName + "(" + argTypes.ToString() + ")");
throw ex;
}
// If we have a name but the object wasn't a component
// that was added to the design container, save the
// name/value relationship in our own nametable.
//
if (name != null)
{
if (_instancesByName == null)
{
_instancesByName = new Hashtable();
_namesByInstance = new Hashtable();
}
_instancesByName[name] = instance;
_namesByInstance[instance] = name;
}
}
return instance;
}
/// Retrieves an instance of a created object of the given name, or
/// null if that object does not exist.
object IDesignerSerializationManager.GetInstance(string name)
{
object instance = null;
if (name == null)
{
throw new ArgumentNullException("name");
}
// Check our local nametable first
//
if (_instancesByName != null)
{
instance = _instancesByName[name];
}
if (instance == null && _loader.LoaderHost != null)
{
instance = _loader.LoaderHost.Container.Components[name];
}
if (instance == null && _resolveNameEventHandler != null)
{
ResolveNameEventArgs e = new ResolveNameEventArgs(name);
_resolveNameEventHandler(this, e);
instance = e.Value;
}
return instance;
}
/// Retrieves a name for the specified object, or null if the object
/// has no name.
string IDesignerSerializationManager.GetName(object value)
{
string name = null;
if (value == null)
{
throw new ArgumentNullException("value");
}
// Check our local nametable first
//
if (_namesByInstance != null)
{
name = (string)_namesByInstance[value];
}
if (name == null && value is IComponent)
{
ISite site = ((IComponent)value).Site;
if (site != null)
{
name = site.Name;
}
}
return name;
}
/// Retrieves a serializer of the requested type for the given
/// object type.
object IDesignerSerializationManager.GetSerializer(Type objectType, Type serializerType)
{
object serializer = null;
if (objectType != null)
{
if (_serializers != null)
{
// I don't double hash here. It will be a very rare day where
// multiple types of serializers will be used in the same scheme.
// We still handle it, but we just don't cache.
//
serializer = _serializers[objectType];
if (serializer != null && !serializerType.IsAssignableFrom(serializer.GetType()))
{
serializer = null;
}
}
// Now actually look in the type's metadata.
//
IDesignerLoaderHost host = _loader.LoaderHost;
if (serializer == null && host != null)
{
AttributeCollection attributes = TypeDescriptor.GetAttributes(objectType);
foreach(Attribute attr in attributes)
{
if (attr is DesignerSerializerAttribute)
{
DesignerSerializerAttribute da = (DesignerSerializerAttribute)attr;
string typeName = da.SerializerBaseTypeName;
// This serializer must support a CodeDomSerializer or we're not interested.
//
if (typeName != null && host.GetType(typeName) == serializerType && da.SerializerTypeName != null && da.SerializerTypeName.Length > 0)
{
Type type = host.GetType(da.SerializerTypeName);
Debug.Assert(type != null, "Type " + objectType.FullName + " has a serializer that we couldn't bind to: " + da.SerializerTypeName);
if (type != null)
{
serializer = Activator.CreateInstance(type, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.CreateInstance, null, null, null);
break;
}
}
}
}
// And stash this little guy for later.
//
if (serializer != null)
{
if (_serializers == null)
{
_serializers = new Hashtable();
}
_serializers[objectType] = serializer;
}
}
}
// Designer serialization providers can override our metadata discovery.
// We loop until we reach steady state. This breaks order dependencies
// by allowing all providers a chance to party on each other's serializers.
//
if (_designerSerializationProviders != null)
{
bool continueLoop = true;
for(int i = 0; continueLoop && i < _designerSerializationProviders.Count; i++)
{
continueLoop = false;
foreach(IDesignerSerializationProvider provider in _designerSerializationProviders)
{
object newSerializer = provider.GetSerializer(this, serializer, objectType, serializerType);
if (newSerializer != null)
{
continueLoop = (serializer != newSerializer);
serializer = newSerializer;
}
}
}
}
return serializer;
}
/// Retrieves a type of the given name.
Type IDesignerSerializationManager.GetType(string typeName)
{
Type t = null;
if (_loader.LoaderHost != null)
{
while (t == null)
{
t = _loader.LoaderHost.GetType(typeName);
if (t == null && typeName != null && typeName.Length > 0)
{
int dotIndex = typeName.LastIndexOf('.');
if (dotIndex == -1 || dotIndex == typeName.Length - 1)
break;
typeName = typeName.Substring(0, dotIndex) + "+" + typeName.Substring(dotIndex + 1, typeName.Length - dotIndex - 1);
}
}
}
return t;
}
/// Removes a previously added serialization provider.
void IDesignerSerializationManager.RemoveSerializationProvider(IDesignerSerializationProvider provider)
{
if (_designerSerializationProviders != null)
{
_designerSerializationProviders.Remove(provider);
}
}
/// Reports a non-fatal error in serialization. The serialization
/// manager may implement a logging scheme to alert the caller
/// to all non-fatal errors at once. If it doesn't, it should
/// immediately throw in this method, which should abort
/// serialization.
/// Serialization may continue after calling this function.
void IDesignerSerializationManager.ReportError(object errorInformation)
{
if (errorInformation != null)
{
if (_errorList == null)
{
_errorList = new ArrayList();
}
_errorList.Add(errorInformation);
}
}
/// Provides a way to set the name of an existing object.
/// This is useful when it is necessary to create an
/// instance of an object without going through CreateInstance.
/// An exception will be thrown if you try to rename an existing
/// object or if you try to give a new object a name that
/// is already taken.
void IDesignerSerializationManager.SetName(object instance, string name)
{
if (instance == null)
{
throw new ArgumentNullException("instance");
}
if (name == null)
{
throw new ArgumentNullException("name");
}
if (_instancesByName == null)
{
_instancesByName = new Hashtable();
_namesByInstance = new Hashtable();
}
if (_instancesByName[name] != null)
{
Exception ex = new ArgumentException("Designer Loader name " + name + " in use.");
throw ex;
}
if (_namesByInstance[instance] != null)
{
Exception ex = new ArgumentException("Designer loader object has name " + name + ".");
throw ex;
}
_instancesByName[name] = instance;
_namesByInstance[instance] = name;
}
/// Retrieves the requested service.
object IServiceProvider.GetService(Type serviceType)
{
return _loader.GetService(serviceType);
}
}
}