context: //
  // System.Data.Common.DbDataPermission.cs
  //
  // Authors:
  //	Rodrigo Moya (rodrigo@ximian.com)
  //	Tim Coleman (tim@timcoleman.com)
  //	Sebastien Pouliot  <sebastien@ximian.com>
  //
  // (C) Ximian, Inc
  // Copyright (C) Tim Coleman, 2002-2003
  // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
  //
  // Permission is hereby granted, free of charge, to any person obtaining
  // a copy of this software and associated documentation files (the
  // "Software"), to deal in the Software without restriction, including
  // without limitation the rights to use, copy, modify, merge, publish,
  // distribute, sublicense, and/or sell copies of the Software, and to
  // permit persons to whom the Software is furnished to do so, subject to
  // the following conditions:
  // 
  // The above copyright notice and this permission notice shall be
  // included in all copies or substantial portions of the Software.
  // 
  // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
  // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
  // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
  // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  //
  
  using System.Collections;
  using System.Security;
  using System.Security.Permissions;
  
  namespace System.Data.Common {
  
  	[Serializable]
  	public abstract class DBDataPermission : CodeAccessPermission, IUnrestrictedPermission {
  
  		#region Fields
  
  		private const int version = 1;
  
  		private bool allowBlankPassword;
  		private PermissionState state;
  		private Hashtable _connections;
  
  		#endregion // Fields
  
  		#region Constructors
  
  #if NET_2_0
  		[Obsolete ("use DBDataPermission (PermissionState.None)", true)]
  #endif
  		protected DBDataPermission () 
  			: this (PermissionState.None)
  		{
  		}
  
  		protected DBDataPermission (DBDataPermission permission)
  		{
  			if (permission == null)
  				throw new ArgumentNullException ("permission");
  
  			state = permission.state;
  			if (state != PermissionState.Unrestricted) {
  				allowBlankPassword = permission.allowBlankPassword;
  				_connections = (Hashtable) permission._connections.Clone ();
  			}
  		}
  
  		protected DBDataPermission (DBDataPermissionAttribute permissionAttribute)
  		{
  			if (permissionAttribute == null)
  				throw new ArgumentNullException ("permissionAttribute");
  
  			_connections = new Hashtable ();
  			if (permissionAttribute.Unrestricted) {
  				state = PermissionState.Unrestricted;
  			}
  			else {
  				state = PermissionState.None;
  				allowBlankPassword = permissionAttribute.AllowBlankPassword;
  				if (permissionAttribute.ConnectionString.Length > 0) {
  					Add (permissionAttribute.ConnectionString, permissionAttribute.KeyRestrictions, 
  						permissionAttribute.KeyRestrictionBehavior);
  				}
  			}
  		}
  
  		protected DBDataPermission (PermissionState state) 
  		{
  			this.state = PermissionHelper.CheckPermissionState (state, true);
  			_connections = new Hashtable ();
  		}
  
  #if NET_2_0
  		[Obsolete ("use DBDataPermission (PermissionState.None)", true)]
  		protected 
  #else
  		public
  #endif
  		DBDataPermission (PermissionState state, bool allowBlankPassword)
  			: this (state)
  		{
  			this.allowBlankPassword = allowBlankPassword;
  		}
  
  		#endregion // Constructors
  
  		#region Properties
  
  		public bool AllowBlankPassword {
  			get { return allowBlankPassword; }
  			set { allowBlankPassword = value; }
  		}
  		
  		#endregion // Properties
  
  		#region Methods
  
  		public virtual void Add (string connectionString, string restrictions, KeyRestrictionBehavior behavior)
  		{
  			state = PermissionState.None;
  			_connections [connectionString] = new object [2] { restrictions, behavior };
  		}
  
  		protected void Clear ()
  		{
  			_connections.Clear ();
  		}
  
  		public override IPermission Copy () 
  		{
  			DBDataPermission dbdp = CreateInstance ();
  			dbdp.allowBlankPassword = this.allowBlankPassword;
  			dbdp._connections = (Hashtable) this._connections.Clone ();
  			return dbdp;
  		}
  
  		protected virtual DBDataPermission CreateInstance ()
  		{
  			return (DBDataPermission) Activator.CreateInstance (this.GetType (), new object [1] { PermissionState.None });
  		}
  
  		public override void FromXml (SecurityElement securityElement) 
  		{
  			PermissionHelper.CheckSecurityElement (securityElement, "securityElement", version, version);
  			// Note: we do not (yet) care about the return value 
  			// as we only accept version 1 (min/max values)
  
  			state = (PermissionHelper.IsUnrestricted (securityElement) ? 
  				PermissionState.Unrestricted : PermissionState.None);
  
  			allowBlankPassword = false;
  			string blank = securityElement.Attribute ("AllowBlankPassword");
  			if (blank != null) {
  #if NET_2_0
  				// avoid possible exceptions with Fx 2.0
  				if (!Boolean.TryParse (blank, out allowBlankPassword))
  					allowBlankPassword = false;
  #else
  				try {
  					allowBlankPassword = Boolean.Parse (blank);
  				}
  				catch {
  					allowBlankPassword = false;
  				}
  #endif
  			}
  
  			if (securityElement.Children != null) {
  				foreach (SecurityElement child in securityElement.Children) {
  					string connect = child.Attribute ("ConnectionString");
  					string restricts = child.Attribute ("KeyRestrictions");
  					KeyRestrictionBehavior behavior = (KeyRestrictionBehavior) Enum.Parse (
  						typeof (KeyRestrictionBehavior), child.Attribute ("KeyRestrictionBehavior"));
  
  					if ((connect != null) && (connect.Length > 0))
  						Add (connect, restricts, behavior);
  				}
  			}
  		}
  
  		public override IPermission Intersect (IPermission target) 
  		{
  			// FIXME: restrictions not completely implemented - nor documented
  			DBDataPermission dbdp = Cast (target);
  			if (dbdp == null)
  				return null;
  			if (IsUnrestricted ()) {
  				if (dbdp.IsUnrestricted ()) {
  					DBDataPermission u = CreateInstance ();
  					u.state = PermissionState.Unrestricted;
  					return u;
  				}
  				return dbdp.Copy ();
  			}
  			if (dbdp.IsUnrestricted ())
  				return Copy ();
  			if (IsEmpty () || dbdp.IsEmpty ())
  				return null;
  
  			DBDataPermission p = CreateInstance ();
  			p.allowBlankPassword = (allowBlankPassword && dbdp.allowBlankPassword);
  			foreach (DictionaryEntry de in _connections) {
  				object o = dbdp._connections [de.Key];
  				if (o != null)
  					p._connections.Add (de.Key, de.Value);
  			}
  			return (p._connections.Count > 0) ? p : null;
  		}
  
  		public override bool IsSubsetOf (IPermission target) 
  		{
  			// FIXME: restrictions not completely implemented - nor documented
  			DBDataPermission dbdp = Cast (target);
  			if (dbdp == null)
  				return IsEmpty ();
  			if (dbdp.IsUnrestricted ())
  				return true;
  			if (IsUnrestricted ())
  				return dbdp.IsUnrestricted ();
  
  			if (allowBlankPassword && !dbdp.allowBlankPassword)
  				return false;
  			if (_connections.Count > dbdp._connections.Count)
  				return false;
  
  			foreach (DictionaryEntry de in _connections) {
  				object o = dbdp._connections [de.Key];
  				if (o == null)
  					return false;
  				// FIXME: this is a subset of what is required
  				// it seems that we must process both the connect string
  				// and the restrictions - but this has other effects :-/
  			}
  			return true;
  		}
  
  		public bool IsUnrestricted () 
  		{
  			return (state == PermissionState.Unrestricted);
  		}
  
  		public override SecurityElement ToXml ()
  		{
  			SecurityElement se = PermissionHelper.Element (this.GetType (), version);
  			if (IsUnrestricted ()) {
  				se.AddAttribute ("Unrestricted", "true");
  			}
  			else {
  				// attribute is present for both True and False
  				se.AddAttribute ("AllowBlankPassword", allowBlankPassword.ToString ());
  
gt: 				foreach (DictionaryEntry de in _connections) {