2 // System.Runtime.Remoting.RemotingConfiguration.cs
4 // Author: Jaime Anguiano Olarra (jaime@gnome.org)
5 // Lluis Sanchez Gual (lluis@ideary.com)
7 // (C) 2002, Jaime Anguiano Olarra
11 // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
13 // Permission is hereby granted, free of charge, to any person obtaining
14 // a copy of this software and associated documentation files (the
15 // "Software"), to deal in the Software without restriction, including
16 // without limitation the rights to use, copy, modify, merge, publish,
17 // distribute, sublicense, and/or sell copies of the Software, and to
18 // permit persons to whom the Software is furnished to do so, subject to
19 // the following conditions:
21 // The above copyright notice and this permission notice shall be
22 // included in all copies or substantial portions of the Software.
24 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
25 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
26 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
27 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
28 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
29 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
30 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
35 using System.Reflection;
36 using System.Collections;
37 using System.Runtime.Remoting.Activation;
38 using System.Runtime.Remoting.Channels;
39 using System.Runtime.Remoting.Lifetime;
42 namespace System.Runtime.Remoting
44 public class RemotingConfiguration
47 // Private constructor: nobody instantiates this.
49 private RemotingConfiguration ()
53 static string applicationID = null;
54 static string applicationName = null;
55 static string configFile = "";
56 static SmallXmlParser parser = null;
57 static string processGuid = null;
58 static bool defaultConfigRead = false;
59 static bool defaultDelayedConfigRead = false;
60 static string _errorMode;
62 static Hashtable wellKnownClientEntries = new Hashtable();
63 static Hashtable activatedClientEntries = new Hashtable();
64 static Hashtable wellKnownServiceEntries = new Hashtable();
65 static Hashtable activatedServiceEntries = new Hashtable();
67 static Hashtable channelTemplates = new Hashtable ();
68 static Hashtable clientProviderTemplates = new Hashtable ();
69 static Hashtable serverProviderTemplates = new Hashtable ();
72 // At this time the ID will be the application name
73 public static string ApplicationId
77 applicationID = ApplicationName;
82 public static string ApplicationName
84 get { return applicationName; }
85 set { applicationName = value; }
88 public static string ProcessId
91 if (processGuid == null)
92 processGuid = AppDomain.GetProcessGuid ();
101 public static void Configure (string filename)
103 lock (channelTemplates)
105 if (!defaultConfigRead)
107 ReadConfigFile (Environment.GetMachineConfigPath ());
108 defaultConfigRead = true;
111 if (filename != null)
112 ReadConfigFile (filename);
116 private static void ReadConfigFile (string filename)
120 SmallXmlParser parser = new SmallXmlParser ();
121 using (TextReader rreader = new StreamReader (filename)) {
122 ConfigHandler handler = new ConfigHandler (false);
123 parser.Parse (rreader, handler);
128 throw new RemotingException ("Configuration file '" + filename + "' could not be loaded: " + ex.Message, ex);
132 internal static void LoadDefaultDelayedChannels ()
134 lock (channelTemplates)
136 if (defaultDelayedConfigRead || defaultConfigRead) return;
138 SmallXmlParser parser = new SmallXmlParser ();
139 using (TextReader rreader = new StreamReader (Environment.GetMachineConfigPath ())) {
140 ConfigHandler handler = new ConfigHandler (true);
141 parser.Parse (rreader, handler);
143 defaultDelayedConfigRead = true;
147 public static ActivatedClientTypeEntry[] GetRegisteredActivatedClientTypes ()
149 lock (channelTemplates)
151 ActivatedClientTypeEntry[] entries = new ActivatedClientTypeEntry[activatedClientEntries.Count];
152 activatedClientEntries.Values.CopyTo (entries,0);
157 public static ActivatedServiceTypeEntry[] GetRegisteredActivatedServiceTypes ()
159 lock (channelTemplates)
161 ActivatedServiceTypeEntry[] entries = new ActivatedServiceTypeEntry[activatedServiceEntries.Count];
162 activatedServiceEntries.Values.CopyTo (entries,0);
167 public static WellKnownClientTypeEntry[] GetRegisteredWellKnownClientTypes ()
169 lock (channelTemplates)
171 WellKnownClientTypeEntry[] entries = new WellKnownClientTypeEntry[wellKnownClientEntries.Count];
172 wellKnownClientEntries.Values.CopyTo (entries,0);
177 public static WellKnownServiceTypeEntry[] GetRegisteredWellKnownServiceTypes ()
179 lock (channelTemplates)
181 WellKnownServiceTypeEntry[] entries = new WellKnownServiceTypeEntry[wellKnownServiceEntries.Count];
182 wellKnownServiceEntries.Values.CopyTo (entries,0);
187 public static bool IsActivationAllowed (Type serverType)
189 lock (channelTemplates)
191 return activatedServiceEntries.ContainsKey (serverType);
195 public static ActivatedClientTypeEntry IsRemotelyActivatedClientType (Type serviceType)
197 lock (channelTemplates)
199 return activatedClientEntries [serviceType] as ActivatedClientTypeEntry;
203 public static ActivatedClientTypeEntry IsRemotelyActivatedClientType (string typeName, string assemblyName)
205 return IsRemotelyActivatedClientType (Assembly.Load(assemblyName).GetType (typeName));
208 public static WellKnownClientTypeEntry IsWellKnownClientType (Type serviceType)
210 lock (channelTemplates)
212 return wellKnownClientEntries [serviceType] as WellKnownClientTypeEntry;
216 public static WellKnownClientTypeEntry IsWellKnownClientType (string typeName, string assemblyName)
218 return IsWellKnownClientType (Assembly.Load(assemblyName).GetType (typeName));
221 public static void RegisterActivatedClientType (ActivatedClientTypeEntry entry)
223 lock (channelTemplates)
225 if (wellKnownClientEntries.ContainsKey (entry.ObjectType) || activatedClientEntries.ContainsKey (entry.ObjectType))
226 throw new RemotingException ("Attempt to redirect activation of type '" + entry.ObjectType.FullName + "' which is already redirected.");
228 activatedClientEntries[entry.ObjectType] = entry;
229 ActivationServices.EnableProxyActivation (entry.ObjectType, true);
233 public static void RegisterActivatedClientType (Type type, string appUrl)
235 if (type == null) throw new ArgumentNullException ("type");
236 if (appUrl == null) throw new ArgumentNullException ("appUrl");
238 RegisterActivatedClientType (new ActivatedClientTypeEntry (type, appUrl));
241 public static void RegisterActivatedServiceType (ActivatedServiceTypeEntry entry)
243 lock (channelTemplates)
245 activatedServiceEntries.Add (entry.ObjectType, entry);
249 public static void RegisterActivatedServiceType (Type type)
251 RegisterActivatedServiceType (new ActivatedServiceTypeEntry (type));
254 public static void RegisterWellKnownClientType (Type type, string objectUrl)
256 if (type == null) throw new ArgumentNullException ("type");
257 if (objectUrl == null) throw new ArgumentNullException ("objectUrl");
259 RegisterWellKnownClientType (new WellKnownClientTypeEntry (type, objectUrl));
262 public static void RegisterWellKnownClientType (WellKnownClientTypeEntry entry)
264 lock (channelTemplates)
266 if (wellKnownClientEntries.ContainsKey (entry.ObjectType) || activatedClientEntries.ContainsKey (entry.ObjectType))
267 throw new RemotingException ("Attempt to redirect activation of type '" + entry.ObjectType.FullName + "' which is already redirected.");
269 wellKnownClientEntries[entry.ObjectType] = entry;
270 ActivationServices.EnableProxyActivation (entry.ObjectType, true);
274 public static void RegisterWellKnownServiceType (Type type, string objectUrl, WellKnownObjectMode mode)
276 RegisterWellKnownServiceType (new WellKnownServiceTypeEntry (type, objectUrl, mode));
279 public static void RegisterWellKnownServiceType (WellKnownServiceTypeEntry entry)
281 lock (channelTemplates)
283 wellKnownServiceEntries [entry.ObjectUri] = entry;
284 RemotingServices.CreateWellKnownServerIdentity (entry.ObjectType, entry.ObjectUri, entry.Mode);
288 internal static void RegisterChannelTemplate (ChannelData channel)
290 channelTemplates [channel.Id] = channel;
293 internal static void RegisterClientProviderTemplate (ProviderData prov)
295 clientProviderTemplates [prov.Id] = prov;
298 internal static void RegisterServerProviderTemplate (ProviderData prov)
300 serverProviderTemplates [prov.Id] = prov;
303 internal static void RegisterChannels (ArrayList channels, bool onlyDelayed)
305 foreach (ChannelData channel in channels)
307 if (onlyDelayed && channel.DelayLoadAsClientChannel != "true")
310 if (defaultDelayedConfigRead && channel.DelayLoadAsClientChannel == "true")
313 if (channel.Ref != null)
315 ChannelData template = (ChannelData) channelTemplates [channel.Ref];
316 if (template == null) throw new RemotingException ("Channel template '" + channel.Ref + "' not found");
317 channel.CopyFrom (template);
320 foreach (ProviderData prov in channel.ServerProviders)
322 if (prov.Ref != null)
324 ProviderData template = (ProviderData) serverProviderTemplates [prov.Ref];
325 if (template == null) throw new RemotingException ("Provider template '" + prov.Ref + "' not found");
326 prov.CopyFrom (template);
330 foreach (ProviderData prov in channel.ClientProviders)
332 if (prov.Ref != null)
334 ProviderData template = (ProviderData) clientProviderTemplates [prov.Ref];
335 if (template == null) throw new RemotingException ("Provider template '" + prov.Ref + "' not found");
336 prov.CopyFrom (template);
340 ChannelServices.RegisterChannelConfig (channel);
344 internal static void RegisterTypes (ArrayList types)
346 foreach (TypeEntry type in types)
348 if (type is ActivatedClientTypeEntry)
349 RegisterActivatedClientType ((ActivatedClientTypeEntry)type);
350 else if (type is ActivatedServiceTypeEntry)
351 RegisterActivatedServiceType ((ActivatedServiceTypeEntry)type);
352 else if (type is WellKnownClientTypeEntry)
353 RegisterWellKnownClientType ((WellKnownClientTypeEntry)type);
354 else if (type is WellKnownServiceTypeEntry)
355 RegisterWellKnownServiceType ((WellKnownServiceTypeEntry)type);
360 public static bool CustomErrorsEnabled (bool isLocalRequest)
362 if (_errorMode == "off") return false;
363 if (_errorMode == "on") return true;
364 return !isLocalRequest;
368 internal static void SetCustomErrorsMode (string mode)
371 throw new RemotingException ("mode attribute is required");
373 // the mode is case insensitive
374 string m = mode.ToLower ();
376 if (m != "on" && m != "off" && m != "remoteonly")
377 throw new RemotingException ("Invalid custom error mode: " + mode);
383 /***************************************************************
384 * Internal classes used by RemotingConfiguration.Configure () *
385 ***************************************************************/
387 internal class ConfigHandler : SmallXmlParser.IContentHandler
389 ArrayList typeEntries = new ArrayList ();
390 ArrayList channelInstances = new ArrayList ();
392 ChannelData currentChannel = null;
393 Stack currentProviderData = null;
395 string currentClientUrl = null;
398 string currentXmlPath = "";
399 bool onlyDelayedChannels;
401 public ConfigHandler (bool onlyDelayedChannels)
403 this.onlyDelayedChannels = onlyDelayedChannels;
406 void ValidatePath (string element, params string[] paths)
408 foreach (string path in paths)
409 if (CheckPath (path)) return;
411 throw new RemotingException ("Element " + element + " not allowed in this context");
414 bool CheckPath (string path)
416 if (path.StartsWith ("/"))
417 return path == currentXmlPath;
419 return currentXmlPath.EndsWith (path);
422 public void OnStartParsing (SmallXmlParser parser) {}
424 public void OnProcessingInstruction (string name, string text) {}
426 public void OnIgnorableWhitespace (string s) {}
428 public void OnStartElement (string name, SmallXmlParser.IAttrList attrs)
432 if (currentXmlPath.StartsWith ("/configuration/system.runtime.remoting"))
433 ParseElement (name, attrs);
435 currentXmlPath += "/" + name;
439 throw new RemotingException ("Error in element " + name + ": " + ex.Message, ex);
443 public void ParseElement (string name, SmallXmlParser.IAttrList attrs)
445 if (currentProviderData != null)
447 ReadCustomProviderData (name, attrs);
454 ValidatePath (name, "system.runtime.remoting");
455 if (attrs.Names.Length > 0)
456 appName = attrs.Values[0];
460 ValidatePath (name, "application");
461 ReadLifetine (attrs);
465 ValidatePath (name, "system.runtime.remoting", "application");
469 ValidatePath (name, "channels");
470 if (currentXmlPath.IndexOf ("application") != -1)
471 ReadChannel (attrs, false);
473 ReadChannel (attrs, true);
476 case "serverProviders":
477 ValidatePath (name, "channelSinkProviders", "channel");
480 case "clientProviders":
481 ValidatePath (name, "channelSinkProviders", "channel");
488 if (CheckPath ("application/channels/channel/serverProviders") ||
489 CheckPath ("channels/channel/serverProviders"))
491 prov = ReadProvider (name, attrs, false);
492 currentChannel.ServerProviders.Add (prov);
494 else if (CheckPath ("application/channels/channel/clientProviders") ||
495 CheckPath ("channels/channel/clientProviders"))
497 prov = ReadProvider (name, attrs, false);
498 currentChannel.ClientProviders.Add (prov);
500 else if (CheckPath ("channelSinkProviders/serverProviders"))
502 prov = ReadProvider (name, attrs, true);
503 RemotingConfiguration.RegisterServerProviderTemplate (prov);
505 else if (CheckPath ("channelSinkProviders/clientProviders"))
507 prov = ReadProvider (name, attrs, true);
508 RemotingConfiguration.RegisterClientProviderTemplate (prov);
515 ValidatePath (name, "application");
516 currentClientUrl = attrs.GetValue ("url");
520 ValidatePath (name, "application");
524 ValidatePath (name, "client", "service");
525 if (CheckPath ("client"))
526 ReadClientWellKnown (attrs);
528 ReadServiceWellKnown (attrs);
532 ValidatePath (name, "client", "service");
533 if (CheckPath ("client"))
534 ReadClientActivated (attrs);
536 ReadServiceActivated (attrs);
540 ValidatePath (name, "application");
543 case "interopXmlType":
544 ValidatePath (name, "soapInterop");
545 ReadInteropXml (attrs, false);
548 case "interopXmlElement":
549 ValidatePath (name, "soapInterop");
550 ReadInteropXml (attrs, false);
554 ValidatePath (name, "soapInterop");
559 ValidatePath (name, "system.runtime.remoting");
562 case "channelSinkProviders":
563 ValidatePath (name, "system.runtime.remoting");
567 ValidatePath (name, "system.runtime.remoting");
568 RemotingConfiguration.SetCustomErrorsMode (attrs.GetValue ("mode"));
572 throw new RemotingException ("Element '" + name + "' is not valid in system.remoting.configuration section");
576 public void OnEndElement (string name)
578 if (currentProviderData != null)
580 currentProviderData.Pop ();
581 if (currentProviderData.Count == 0)
582 currentProviderData = null;
585 currentXmlPath = currentXmlPath.Substring (0, currentXmlPath.Length - name.Length - 1);
588 void ReadCustomProviderData (string name, SmallXmlParser.IAttrList attrs)
590 SinkProviderData parent = (SinkProviderData) currentProviderData.Peek ();
592 SinkProviderData data = new SinkProviderData (name);
593 for (int i=0; i < attrs.Names.Length; ++i)
594 data.Properties [attrs.Names[i]] = attrs.GetValue (i);
596 parent.Children.Add (data);
597 currentProviderData.Push (data);
600 void ReadLifetine (SmallXmlParser.IAttrList attrs)
602 for (int i=0; i < attrs.Names.Length; ++i) {
603 switch (attrs.Names[i]) {
605 LifetimeServices.LeaseTime = ParseTime (attrs.GetValue(i));
607 case "sponsorshipTimeout":
608 LifetimeServices.SponsorshipTimeout = ParseTime (attrs.GetValue(i));
610 case "renewOnCallTime":
611 LifetimeServices.RenewOnCallTime = ParseTime (attrs.GetValue(i));
613 case "leaseManagerPollTime":
614 LifetimeServices.LeaseManagerPollTime = ParseTime (attrs.GetValue(i));
617 throw new RemotingException ("Invalid attribute: " + attrs.Names[i]);
622 TimeSpan ParseTime (string s)
624 if (s == "" || s == null) throw new RemotingException ("Invalid time value");
626 int i = s.IndexOfAny (new char[] { 'D','H','M','S' });
632 unit = s.Substring (i);
633 s = s.Substring (0,i);
638 val = double.Parse (s);
641 throw new RemotingException ("Invalid time value: " + s);
644 if (unit == "D") return TimeSpan.FromDays (val);
645 if (unit == "H") return TimeSpan.FromHours (val);
646 if (unit == "M") return TimeSpan.FromMinutes (val);
647 if (unit == "S") return TimeSpan.FromSeconds (val);
648 if (unit == "MS") return TimeSpan.FromMilliseconds (val);
649 throw new RemotingException ("Invalid time unit: " + unit);
652 void ReadChannel (SmallXmlParser.IAttrList attrs, bool isTemplate)
654 ChannelData channel = new ChannelData ();
656 for (int i=0; i < attrs.Names.Length; ++i)
658 string at = attrs.Names[i];
659 string val = attrs.Values[i];
661 if (at == "ref" && !isTemplate)
663 else if (at == "delayLoadAsClientChannel")
664 channel.DelayLoadAsClientChannel = val;
665 else if (at == "id" && isTemplate)
667 else if (at == "type")
670 channel.CustomProperties.Add (at, val);
675 if (channel.Id == null) throw new RemotingException ("id attribute is required");
676 if (channel.Type == null) throw new RemotingException ("id attribute is required");
677 RemotingConfiguration.RegisterChannelTemplate (channel);
680 channelInstances.Add (channel);
682 currentChannel = channel;
685 ProviderData ReadProvider (string name, SmallXmlParser.IAttrList attrs, bool isTemplate)
687 ProviderData prov = (name == "provider") ? new ProviderData () : new FormatterData ();
688 SinkProviderData data = new SinkProviderData ("root");
689 prov.CustomData = data.Children;
691 currentProviderData = new Stack ();
692 currentProviderData.Push (data);
694 for (int i=0; i < attrs.Names.Length; ++i)
696 string at = attrs.Names[i];
697 string val = attrs.Values[i];
699 if (at == "id" && isTemplate)
701 else if (at == "type")
703 else if (at == "ref" && !isTemplate)
706 prov.CustomProperties.Add (at, val);
709 if (prov.Id == null && isTemplate) throw new RemotingException ("id attribute is required");
713 void ReadClientActivated (SmallXmlParser.IAttrList attrs)
715 string type = GetNotNull (attrs, "type");
716 string assm = ExtractAssembly (ref type);
718 if (currentClientUrl == null || currentClientUrl == "")
719 throw new RemotingException ("url attribute is required in client element when it contains activated entries");
721 typeEntries.Add (new ActivatedClientTypeEntry (type, assm, currentClientUrl));
724 void ReadServiceActivated (SmallXmlParser.IAttrList attrs)
726 string type = GetNotNull (attrs, "type");
727 string assm = ExtractAssembly (ref type);
729 typeEntries.Add (new ActivatedServiceTypeEntry (type, assm));
732 void ReadClientWellKnown (SmallXmlParser.IAttrList attrs)
734 string url = GetNotNull (attrs, "url");
735 string type = GetNotNull (attrs, "type");
736 string assm = ExtractAssembly (ref type);
738 typeEntries.Add (new WellKnownClientTypeEntry (type, assm, url));
741 void ReadServiceWellKnown (SmallXmlParser.IAttrList attrs)
743 string objectUri = GetNotNull (attrs, "objectUri");
744 string smode = GetNotNull (attrs, "mode");
745 string type = GetNotNull (attrs, "type");
746 string assm = ExtractAssembly (ref type);
748 WellKnownObjectMode mode;
749 if (smode == "SingleCall") mode = WellKnownObjectMode.SingleCall;
750 else if (smode == "Singleton") mode = WellKnownObjectMode.Singleton;
751 else throw new RemotingException ("wellknown object mode '" + smode + "' is invalid");
753 typeEntries.Add (new WellKnownServiceTypeEntry (type, assm, objectUri, mode));
756 void ReadInteropXml (SmallXmlParser.IAttrList attrs, bool isElement)
758 Type t = Type.GetType (GetNotNull (attrs, "clr"));
759 string[] xmlName = GetNotNull (attrs, "xml").Split (',');
760 string localName = xmlName [0].Trim ();
761 string ns = xmlName.Length > 0 ? xmlName[1].Trim() : null;
763 if (isElement) SoapServices.RegisterInteropXmlElement (localName, ns, t);
764 else SoapServices.RegisterInteropXmlType (localName, ns, t);
767 void ReadPreload (SmallXmlParser.IAttrList attrs)
769 string type = attrs.GetValue ("type");
770 string assm = attrs.GetValue ("assembly");
772 if (type != null && assm != null)
773 throw new RemotingException ("Type and assembly attributes cannot be specified together");
776 SoapServices.PreLoad (Type.GetType (type));
777 else if (assm != null)
778 SoapServices.PreLoad (Assembly.Load (assm));
780 throw new RemotingException ("Either type or assembly attributes must be specified");
783 string GetNotNull (SmallXmlParser.IAttrList attrs, string name)
785 string value = attrs.GetValue (name);
786 if (value == null || value == "")
787 throw new RemotingException (name + " attribute is required");
791 string ExtractAssembly (ref string type)
793 int i = type.IndexOf (',');
794 if (i == -1) return "";
796 string asm = type.Substring (i+1).Trim();
797 type = type.Substring (0, i).Trim();
801 public void OnChars (string ch) {}
803 public void OnEndParsing (SmallXmlParser parser)
805 RemotingConfiguration.RegisterChannels (channelInstances, onlyDelayedChannels);
806 if (appName != null) RemotingConfiguration.ApplicationName = appName;
808 if (!onlyDelayedChannels)
809 RemotingConfiguration.RegisterTypes (typeEntries);
814 /*******************************************************************
815 * Internal data structures used by ConfigHandler, to store *
816 * machine.config's remoting related data. *
817 * If having them implemented this way, makes configuration too *
818 * slow, we can use string arrays. *
819 *******************************************************************/
821 internal class ChannelData {
823 internal string Type;
825 internal string DelayLoadAsClientChannel;
827 ArrayList _serverProviders = new ArrayList ();
828 ArrayList _clientProviders = new ArrayList ();
829 Hashtable _customProperties = new Hashtable ();
831 internal ArrayList ServerProviders {
833 if (_serverProviders == null) _serverProviders = new ArrayList ();
834 return _serverProviders;
838 public ArrayList ClientProviders {
840 if (_clientProviders == null) _clientProviders = new ArrayList ();
841 return _clientProviders;
845 public Hashtable CustomProperties {
847 if (_customProperties == null) _customProperties = new Hashtable ();
848 return _customProperties;
852 public void CopyFrom (ChannelData other)
854 if (Ref == null) Ref = other.Ref;
855 if (Id == null) Id = other.Id;
856 if (Type == null) Type = other.Type;
857 if (DelayLoadAsClientChannel == null) DelayLoadAsClientChannel = other.DelayLoadAsClientChannel;
859 if (other._customProperties != null)
861 foreach (DictionaryEntry entry in other._customProperties)
862 if (!CustomProperties.ContainsKey (entry.Key))
863 CustomProperties [entry.Key] = entry.Value;
866 if (_serverProviders == null && other._serverProviders != null)
868 foreach (ProviderData prov in other._serverProviders)
870 ProviderData np = new ProviderData();
872 ServerProviders.Add (np);
876 if (_clientProviders == null && other._clientProviders != null)
878 foreach (ProviderData prov in other._clientProviders)
880 ProviderData np = new ProviderData();
882 ClientProviders.Add (np);
888 internal class ProviderData {
890 internal string Type;
893 internal Hashtable CustomProperties = new Hashtable ();
894 internal IList CustomData;
896 public void CopyFrom (ProviderData other)
898 if (Ref == null) Ref = other.Ref;
899 if (Id == null) Id = other.Id;
900 if (Type == null) Type = other.Type;
902 foreach (DictionaryEntry entry in other.CustomProperties)
903 if (!CustomProperties.ContainsKey (entry.Key))
904 CustomProperties [entry.Key] = entry.Value;
906 if (other.CustomData != null)
908 if (CustomData == null) CustomData = new ArrayList ();
909 foreach (SinkProviderData data in other.CustomData)
910 CustomData.Add (data);
915 internal class FormatterData: ProviderData {