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.
34 using System.Globalization;
36 using System.Reflection;
37 using System.Collections;
38 using System.Runtime.Remoting.Activation;
39 using System.Runtime.Remoting.Channels;
40 using System.Runtime.Remoting.Lifetime;
43 namespace System.Runtime.Remoting
45 [System.Runtime.InteropServices.ComVisible (true)]
46 public static class RemotingConfiguration
49 static string applicationID = null;
50 static string applicationName = null;
51 // static string configFile = "";
52 // static SmallXmlParser parser = null;
53 static string processGuid = null;
54 static bool defaultConfigRead = false;
55 static bool defaultDelayedConfigRead = false;
56 static string _errorMode;
58 static Hashtable wellKnownClientEntries = new Hashtable();
59 static Hashtable activatedClientEntries = new Hashtable();
60 static Hashtable wellKnownServiceEntries = new Hashtable();
61 static Hashtable activatedServiceEntries = new Hashtable();
63 static Hashtable channelTemplates = new Hashtable ();
64 static Hashtable clientProviderTemplates = new Hashtable ();
65 static Hashtable serverProviderTemplates = new Hashtable ();
68 // At this time the ID will be the application name
69 public static string ApplicationId
73 applicationID = ApplicationName;
78 public static string ApplicationName
80 get { return applicationName; }
81 set { applicationName = value; }
85 public static CustomErrorsModes CustomErrorsMode
87 get { throw new NotImplementedException (); }
88 set { throw new NotImplementedException (); }
91 public static string ProcessId
94 if (processGuid == null)
95 processGuid = AppDomain.GetProcessGuid ();
104 [MonoTODO ("ensureSecurity support has not been implemented")]
105 public static void Configure (string filename, bool ensureSecurity)
107 lock (channelTemplates) {
108 if (!defaultConfigRead) {
109 ReadConfigFile (Environment.GetMachineConfigPath ());
110 defaultConfigRead = true;
113 if (filename != null)
114 ReadConfigFile (filename);
118 [Obsolete ("Use Configure(String,Boolean)")]
119 public static void Configure (string filename)
121 Configure (filename, false);
124 private static void ReadConfigFile (string filename)
128 SmallXmlParser parser = new SmallXmlParser ();
129 using (TextReader rreader = new StreamReader (filename)) {
130 ConfigHandler handler = new ConfigHandler (false);
131 parser.Parse (rreader, handler);
136 throw new RemotingException ("Configuration file '" + filename + "' could not be loaded: " + ex.Message, ex);
140 internal static void LoadDefaultDelayedChannels ()
142 lock (channelTemplates)
144 if (defaultDelayedConfigRead || defaultConfigRead) return;
146 SmallXmlParser parser = new SmallXmlParser ();
147 using (TextReader rreader = new StreamReader (Environment.GetMachineConfigPath ())) {
148 ConfigHandler handler = new ConfigHandler (true);
149 parser.Parse (rreader, handler);
151 defaultDelayedConfigRead = true;
155 public static ActivatedClientTypeEntry[] GetRegisteredActivatedClientTypes ()
157 lock (channelTemplates)
159 ActivatedClientTypeEntry[] entries = new ActivatedClientTypeEntry[activatedClientEntries.Count];
160 activatedClientEntries.Values.CopyTo (entries,0);
165 public static ActivatedServiceTypeEntry[] GetRegisteredActivatedServiceTypes ()
167 lock (channelTemplates)
169 ActivatedServiceTypeEntry[] entries = new ActivatedServiceTypeEntry[activatedServiceEntries.Count];
170 activatedServiceEntries.Values.CopyTo (entries,0);
175 public static WellKnownClientTypeEntry[] GetRegisteredWellKnownClientTypes ()
177 lock (channelTemplates)
179 WellKnownClientTypeEntry[] entries = new WellKnownClientTypeEntry[wellKnownClientEntries.Count];
180 wellKnownClientEntries.Values.CopyTo (entries,0);
185 public static WellKnownServiceTypeEntry[] GetRegisteredWellKnownServiceTypes ()
187 lock (channelTemplates)
189 WellKnownServiceTypeEntry[] entries = new WellKnownServiceTypeEntry[wellKnownServiceEntries.Count];
190 wellKnownServiceEntries.Values.CopyTo (entries,0);
195 public static bool IsActivationAllowed (Type svrType)
197 lock (channelTemplates)
199 return activatedServiceEntries.ContainsKey (svrType);
203 public static ActivatedClientTypeEntry IsRemotelyActivatedClientType (Type svrType)
205 lock (channelTemplates)
207 return activatedClientEntries [svrType] as ActivatedClientTypeEntry;
211 public static ActivatedClientTypeEntry IsRemotelyActivatedClientType (string typeName, string assemblyName)
213 return IsRemotelyActivatedClientType (Assembly.Load(assemblyName).GetType (typeName));
216 public static WellKnownClientTypeEntry IsWellKnownClientType (Type svrType)
218 lock (channelTemplates)
220 return wellKnownClientEntries [svrType] as WellKnownClientTypeEntry;
224 public static WellKnownClientTypeEntry IsWellKnownClientType (string typeName, string assemblyName)
226 return IsWellKnownClientType (Assembly.Load(assemblyName).GetType (typeName));
229 public static void RegisterActivatedClientType (ActivatedClientTypeEntry entry)
231 lock (channelTemplates)
233 if (wellKnownClientEntries.ContainsKey (entry.ObjectType) || activatedClientEntries.ContainsKey (entry.ObjectType))
234 throw new RemotingException ("Attempt to redirect activation of type '" + entry.ObjectType.FullName + "' which is already redirected.");
236 activatedClientEntries[entry.ObjectType] = entry;
237 ActivationServices.EnableProxyActivation (entry.ObjectType, true);
241 public static void RegisterActivatedClientType (Type type, string appUrl)
243 if (type == null) throw new ArgumentNullException ("type");
244 if (appUrl == null) throw new ArgumentNullException ("appUrl");
246 RegisterActivatedClientType (new ActivatedClientTypeEntry (type, appUrl));
249 public static void RegisterActivatedServiceType (ActivatedServiceTypeEntry entry)
251 lock (channelTemplates)
253 activatedServiceEntries.Add (entry.ObjectType, entry);
257 public static void RegisterActivatedServiceType (Type type)
259 RegisterActivatedServiceType (new ActivatedServiceTypeEntry (type));
262 public static void RegisterWellKnownClientType (Type type, string objectUrl)
264 if (type == null) throw new ArgumentNullException ("type");
265 if (objectUrl == null) throw new ArgumentNullException ("objectUrl");
267 RegisterWellKnownClientType (new WellKnownClientTypeEntry (type, objectUrl));
270 public static void RegisterWellKnownClientType (WellKnownClientTypeEntry entry)
272 lock (channelTemplates)
274 if (wellKnownClientEntries.ContainsKey (entry.ObjectType) || activatedClientEntries.ContainsKey (entry.ObjectType))
275 throw new RemotingException ("Attempt to redirect activation of type '" + entry.ObjectType.FullName + "' which is already redirected.");
277 wellKnownClientEntries[entry.ObjectType] = entry;
278 ActivationServices.EnableProxyActivation (entry.ObjectType, true);
282 public static void RegisterWellKnownServiceType (Type type, string objectUri, WellKnownObjectMode mode)
284 RegisterWellKnownServiceType (new WellKnownServiceTypeEntry (type, objectUri, mode));
287 public static void RegisterWellKnownServiceType (WellKnownServiceTypeEntry entry)
289 lock (channelTemplates)
291 wellKnownServiceEntries [entry.ObjectUri] = entry;
292 RemotingServices.CreateWellKnownServerIdentity (entry.ObjectType, entry.ObjectUri, entry.Mode);
296 internal static void RegisterChannelTemplate (ChannelData channel)
298 channelTemplates [channel.Id] = channel;
301 internal static void RegisterClientProviderTemplate (ProviderData prov)
303 clientProviderTemplates [prov.Id] = prov;
306 internal static void RegisterServerProviderTemplate (ProviderData prov)
308 serverProviderTemplates [prov.Id] = prov;
311 internal static void RegisterChannels (ArrayList channels, bool onlyDelayed)
313 foreach (ChannelData channel in channels)
315 if (onlyDelayed && channel.DelayLoadAsClientChannel != "true")
318 if (defaultDelayedConfigRead && channel.DelayLoadAsClientChannel == "true")
321 if (channel.Ref != null)
323 ChannelData template = (ChannelData) channelTemplates [channel.Ref];
324 if (template == null) throw new RemotingException ("Channel template '" + channel.Ref + "' not found");
325 channel.CopyFrom (template);
328 foreach (ProviderData prov in channel.ServerProviders)
330 if (prov.Ref != null)
332 ProviderData template = (ProviderData) serverProviderTemplates [prov.Ref];
333 if (template == null) throw new RemotingException ("Provider template '" + prov.Ref + "' not found");
334 prov.CopyFrom (template);
338 foreach (ProviderData prov in channel.ClientProviders)
340 if (prov.Ref != null)
342 ProviderData template = (ProviderData) clientProviderTemplates [prov.Ref];
343 if (template == null) throw new RemotingException ("Provider template '" + prov.Ref + "' not found");
344 prov.CopyFrom (template);
348 ChannelServices.RegisterChannelConfig (channel);
352 internal static void RegisterTypes (ArrayList types)
354 foreach (TypeEntry type in types)
356 if (type is ActivatedClientTypeEntry)
357 RegisterActivatedClientType ((ActivatedClientTypeEntry)type);
358 else if (type is ActivatedServiceTypeEntry)
359 RegisterActivatedServiceType ((ActivatedServiceTypeEntry)type);
360 else if (type is WellKnownClientTypeEntry)
361 RegisterWellKnownClientType ((WellKnownClientTypeEntry)type);
362 else if (type is WellKnownServiceTypeEntry)
363 RegisterWellKnownServiceType ((WellKnownServiceTypeEntry)type);
367 public static bool CustomErrorsEnabled (bool isLocalRequest)
369 if (_errorMode == "off") return false;
370 if (_errorMode == "on") return true;
371 return !isLocalRequest;
374 internal static void SetCustomErrorsMode (string mode)
377 throw new RemotingException ("mode attribute is required");
379 // the mode is case insensitive
380 string m = mode.ToLower ();
382 if (m != "on" && m != "off" && m != "remoteonly")
383 throw new RemotingException ("Invalid custom error mode: " + mode);
389 /***************************************************************
390 * Internal classes used by RemotingConfiguration.Configure () *
391 ***************************************************************/
393 internal class ConfigHandler : SmallXmlParser.IContentHandler
395 ArrayList typeEntries = new ArrayList ();
396 ArrayList channelInstances = new ArrayList ();
398 ChannelData currentChannel = null;
399 Stack currentProviderData = null;
401 string currentClientUrl = null;
404 string currentXmlPath = "";
405 bool onlyDelayedChannels;
407 public ConfigHandler (bool onlyDelayedChannels)
409 this.onlyDelayedChannels = onlyDelayedChannels;
412 void ValidatePath (string element, params string[] paths)
414 foreach (string path in paths)
415 if (CheckPath (path)) return;
417 throw new RemotingException ("Element " + element + " not allowed in this context");
420 bool CheckPath (string path)
422 CompareInfo ci = CultureInfo.InvariantCulture.CompareInfo;
423 if (ci.IsPrefix (path, "/", CompareOptions.Ordinal))
424 return path == currentXmlPath;
426 return ci.IsSuffix (currentXmlPath, path, CompareOptions.Ordinal);
429 public void OnStartParsing (SmallXmlParser parser) {}
431 public void OnProcessingInstruction (string name, string text) {}
433 public void OnIgnorableWhitespace (string s) {}
435 public void OnStartElement (string name, SmallXmlParser.IAttrList attrs)
439 if (currentXmlPath.StartsWith ("/configuration/system.runtime.remoting"))
440 ParseElement (name, attrs);
442 currentXmlPath += "/" + name;
446 throw new RemotingException ("Error in element " + name + ": " + ex.Message, ex);
450 public void ParseElement (string name, SmallXmlParser.IAttrList attrs)
452 if (currentProviderData != null)
454 ReadCustomProviderData (name, attrs);
461 ValidatePath (name, "system.runtime.remoting");
462 if (attrs.Names.Length > 0)
463 appName = attrs.Values[0];
467 ValidatePath (name, "application");
468 ReadLifetine (attrs);
472 ValidatePath (name, "system.runtime.remoting", "application");
476 ValidatePath (name, "channels");
477 if (currentXmlPath.IndexOf ("application") != -1)
478 ReadChannel (attrs, false);
480 ReadChannel (attrs, true);
483 case "serverProviders":
484 ValidatePath (name, "channelSinkProviders", "channel");
487 case "clientProviders":
488 ValidatePath (name, "channelSinkProviders", "channel");
495 if (CheckPath ("application/channels/channel/serverProviders") ||
496 CheckPath ("channels/channel/serverProviders"))
498 prov = ReadProvider (name, attrs, false);
499 currentChannel.ServerProviders.Add (prov);
501 else if (CheckPath ("application/channels/channel/clientProviders") ||
502 CheckPath ("channels/channel/clientProviders"))
504 prov = ReadProvider (name, attrs, false);
505 currentChannel.ClientProviders.Add (prov);
507 else if (CheckPath ("channelSinkProviders/serverProviders"))
509 prov = ReadProvider (name, attrs, true);
510 RemotingConfiguration.RegisterServerProviderTemplate (prov);
512 else if (CheckPath ("channelSinkProviders/clientProviders"))
514 prov = ReadProvider (name, attrs, true);
515 RemotingConfiguration.RegisterClientProviderTemplate (prov);
522 ValidatePath (name, "application");
523 currentClientUrl = attrs.GetValue ("url");
527 ValidatePath (name, "application");
531 ValidatePath (name, "client", "service");
532 if (CheckPath ("client"))
533 ReadClientWellKnown (attrs);
535 ReadServiceWellKnown (attrs);
539 ValidatePath (name, "client", "service");
540 if (CheckPath ("client"))
541 ReadClientActivated (attrs);
543 ReadServiceActivated (attrs);
547 ValidatePath (name, "application");
550 case "interopXmlType":
551 ValidatePath (name, "soapInterop");
552 ReadInteropXml (attrs, false);
555 case "interopXmlElement":
556 ValidatePath (name, "soapInterop");
557 ReadInteropXml (attrs, false);
561 ValidatePath (name, "soapInterop");
566 ValidatePath (name, "system.runtime.remoting");
569 case "channelSinkProviders":
570 ValidatePath (name, "system.runtime.remoting");
574 ValidatePath (name, "system.runtime.remoting");
575 RemotingConfiguration.SetCustomErrorsMode (attrs.GetValue ("mode"));
579 throw new RemotingException ("Element '" + name + "' is not valid in system.remoting.configuration section");
583 public void OnEndElement (string name)
585 if (currentProviderData != null)
587 currentProviderData.Pop ();
588 if (currentProviderData.Count == 0)
589 currentProviderData = null;
592 currentXmlPath = currentXmlPath.Substring (0, currentXmlPath.Length - name.Length - 1);
595 void ReadCustomProviderData (string name, SmallXmlParser.IAttrList attrs)
597 SinkProviderData parent = (SinkProviderData) currentProviderData.Peek ();
599 SinkProviderData data = new SinkProviderData (name);
600 for (int i=0; i < attrs.Names.Length; ++i)
601 data.Properties [attrs.Names[i]] = attrs.GetValue (i);
603 parent.Children.Add (data);
604 currentProviderData.Push (data);
607 void ReadLifetine (SmallXmlParser.IAttrList attrs)
609 for (int i=0; i < attrs.Names.Length; ++i) {
610 switch (attrs.Names[i]) {
612 LifetimeServices.LeaseTime = ParseTime (attrs.GetValue(i));
614 case "sponsorshipTimeout":
615 LifetimeServices.SponsorshipTimeout = ParseTime (attrs.GetValue(i));
617 case "renewOnCallTime":
618 LifetimeServices.RenewOnCallTime = ParseTime (attrs.GetValue(i));
620 case "leaseManagerPollTime":
621 LifetimeServices.LeaseManagerPollTime = ParseTime (attrs.GetValue(i));
624 throw new RemotingException ("Invalid attribute: " + attrs.Names[i]);
629 TimeSpan ParseTime (string s)
631 if (s == "" || s == null) throw new RemotingException ("Invalid time value");
633 int i = s.IndexOfAny (new char[] { 'D','H','M','S' });
639 unit = s.Substring (i);
640 s = s.Substring (0,i);
645 val = double.Parse (s);
648 throw new RemotingException ("Invalid time value: " + s);
651 if (unit == "D") return TimeSpan.FromDays (val);
652 if (unit == "H") return TimeSpan.FromHours (val);
653 if (unit == "M") return TimeSpan.FromMinutes (val);
654 if (unit == "S") return TimeSpan.FromSeconds (val);
655 if (unit == "MS") return TimeSpan.FromMilliseconds (val);
656 throw new RemotingException ("Invalid time unit: " + unit);
659 void ReadChannel (SmallXmlParser.IAttrList attrs, bool isTemplate)
661 ChannelData channel = new ChannelData ();
663 for (int i=0; i < attrs.Names.Length; ++i)
665 string at = attrs.Names[i];
666 string val = attrs.Values[i];
668 if (at == "ref" && !isTemplate)
670 else if (at == "delayLoadAsClientChannel")
671 channel.DelayLoadAsClientChannel = val;
672 else if (at == "id" && isTemplate)
674 else if (at == "type")
677 channel.CustomProperties.Add (at, val);
682 if (channel.Id == null) throw new RemotingException ("id attribute is required");
683 if (channel.Type == null) throw new RemotingException ("id attribute is required");
684 RemotingConfiguration.RegisterChannelTemplate (channel);
687 channelInstances.Add (channel);
689 currentChannel = channel;
692 ProviderData ReadProvider (string name, SmallXmlParser.IAttrList attrs, bool isTemplate)
694 ProviderData prov = (name == "provider") ? new ProviderData () : new FormatterData ();
695 SinkProviderData data = new SinkProviderData ("root");
696 prov.CustomData = data.Children;
698 currentProviderData = new Stack ();
699 currentProviderData.Push (data);
701 for (int i=0; i < attrs.Names.Length; ++i)
703 string at = attrs.Names[i];
704 string val = attrs.Values[i];
706 if (at == "id" && isTemplate)
708 else if (at == "type")
710 else if (at == "ref" && !isTemplate)
713 prov.CustomProperties.Add (at, val);
716 if (prov.Id == null && isTemplate) throw new RemotingException ("id attribute is required");
720 void ReadClientActivated (SmallXmlParser.IAttrList attrs)
722 string type = GetNotNull (attrs, "type");
723 string assm = ExtractAssembly (ref type);
725 if (currentClientUrl == null || currentClientUrl == "")
726 throw new RemotingException ("url attribute is required in client element when it contains activated entries");
728 typeEntries.Add (new ActivatedClientTypeEntry (type, assm, currentClientUrl));
731 void ReadServiceActivated (SmallXmlParser.IAttrList attrs)
733 string type = GetNotNull (attrs, "type");
734 string assm = ExtractAssembly (ref type);
736 typeEntries.Add (new ActivatedServiceTypeEntry (type, assm));
739 void ReadClientWellKnown (SmallXmlParser.IAttrList attrs)
741 string url = GetNotNull (attrs, "url");
742 string type = GetNotNull (attrs, "type");
743 string assm = ExtractAssembly (ref type);
745 typeEntries.Add (new WellKnownClientTypeEntry (type, assm, url));
748 void ReadServiceWellKnown (SmallXmlParser.IAttrList attrs)
750 string objectUri = GetNotNull (attrs, "objectUri");
751 string smode = GetNotNull (attrs, "mode");
752 string type = GetNotNull (attrs, "type");
753 string assm = ExtractAssembly (ref type);
755 WellKnownObjectMode mode;
756 if (smode == "SingleCall") mode = WellKnownObjectMode.SingleCall;
757 else if (smode == "Singleton") mode = WellKnownObjectMode.Singleton;
758 else throw new RemotingException ("wellknown object mode '" + smode + "' is invalid");
760 typeEntries.Add (new WellKnownServiceTypeEntry (type, assm, objectUri, mode));
763 void ReadInteropXml (SmallXmlParser.IAttrList attrs, bool isElement)
765 Type t = Type.GetType (GetNotNull (attrs, "clr"));
766 string[] xmlName = GetNotNull (attrs, "xml").Split (',');
767 string localName = xmlName [0].Trim ();
768 string ns = xmlName.Length > 0 ? xmlName[1].Trim() : null;
770 if (isElement) SoapServices.RegisterInteropXmlElement (localName, ns, t);
771 else SoapServices.RegisterInteropXmlType (localName, ns, t);
774 void ReadPreload (SmallXmlParser.IAttrList attrs)
776 string type = attrs.GetValue ("type");
777 string assm = attrs.GetValue ("assembly");
779 if (type != null && assm != null)
780 throw new RemotingException ("Type and assembly attributes cannot be specified together");
783 SoapServices.PreLoad (Type.GetType (type));
784 else if (assm != null)
785 SoapServices.PreLoad (Assembly.Load (assm));
787 throw new RemotingException ("Either type or assembly attributes must be specified");
790 string GetNotNull (SmallXmlParser.IAttrList attrs, string name)
792 string value = attrs.GetValue (name);
793 if (value == null || value == "")
794 throw new RemotingException (name + " attribute is required");
798 string ExtractAssembly (ref string type)
800 int i = type.IndexOf (',');
801 if (i == -1) return "";
803 string asm = type.Substring (i+1).Trim();
804 type = type.Substring (0, i).Trim();
808 public void OnChars (string ch) {}
810 public void OnEndParsing (SmallXmlParser parser)
812 RemotingConfiguration.RegisterChannels (channelInstances, onlyDelayedChannels);
813 if (appName != null) RemotingConfiguration.ApplicationName = appName;
815 if (!onlyDelayedChannels)
816 RemotingConfiguration.RegisterTypes (typeEntries);
821 /*******************************************************************
822 * Internal data structures used by ConfigHandler, to store *
823 * machine.config's remoting related data. *
824 * If having them implemented this way, makes configuration too *
825 * slow, we can use string arrays. *
826 *******************************************************************/
828 internal class ChannelData {
830 internal string Type;
832 internal string DelayLoadAsClientChannel;
834 ArrayList _serverProviders = new ArrayList ();
835 ArrayList _clientProviders = new ArrayList ();
836 Hashtable _customProperties = new Hashtable ();
838 internal ArrayList ServerProviders {
840 if (_serverProviders == null) _serverProviders = new ArrayList ();
841 return _serverProviders;
845 public ArrayList ClientProviders {
847 if (_clientProviders == null) _clientProviders = new ArrayList ();
848 return _clientProviders;
852 public Hashtable CustomProperties {
854 if (_customProperties == null) _customProperties = new Hashtable ();
855 return _customProperties;
859 public void CopyFrom (ChannelData other)
861 if (Ref == null) Ref = other.Ref;
862 if (Id == null) Id = other.Id;
863 if (Type == null) Type = other.Type;
864 if (DelayLoadAsClientChannel == null) DelayLoadAsClientChannel = other.DelayLoadAsClientChannel;
866 if (other._customProperties != null)
868 foreach (DictionaryEntry entry in other._customProperties)
869 if (!CustomProperties.ContainsKey (entry.Key))
870 CustomProperties [entry.Key] = entry.Value;
873 if (_serverProviders == null && other._serverProviders != null)
875 foreach (ProviderData prov in other._serverProviders)
877 ProviderData np = new ProviderData();
879 ServerProviders.Add (np);
883 if (_clientProviders == null && other._clientProviders != null)
885 foreach (ProviderData prov in other._clientProviders)
887 ProviderData np = new ProviderData();
889 ClientProviders.Add (np);
895 internal class ProviderData {
897 internal string Type;
900 internal Hashtable CustomProperties = new Hashtable ();
901 internal IList CustomData;
903 public void CopyFrom (ProviderData other)
905 if (Ref == null) Ref = other.Ref;
906 if (Id == null) Id = other.Id;
907 if (Type == null) Type = other.Type;
909 foreach (DictionaryEntry entry in other.CustomProperties)
910 if (!CustomProperties.ContainsKey (entry.Key))
911 CustomProperties [entry.Key] = entry.Value;
913 if (other.CustomData != null)
915 if (CustomData == null) CustomData = new ArrayList ();
916 foreach (SinkProviderData data in other.CustomData)
917 CustomData.Add (data);
922 internal class FormatterData: ProviderData {