// // System.Data.Common.DbConnectionStringBuilder.cs // // Author: // Sureshkumar T (tsureshkumar@novell.com) // Gert Driesen (drieseng@users.sourceforge.net // // 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. // #if NET_2_0 using System; using System.Collections; using System.Collections.Generic; using System.Collections.ObjectModel; using System.ComponentModel; using System.Data; using System.Data.Common; using System.Reflection; using System.Text; namespace System.Data.Common { public class DbConnectionStringBuilder : IDictionary, ICollection, IEnumerable, ICustomTypeDescriptor { #region Fields readonly Dictionary _dictionary; readonly bool useOdbcRules; #endregion Fields #region Constructors public DbConnectionStringBuilder () : this (false) { } public DbConnectionStringBuilder (bool useOdbcRules) { this.useOdbcRules = useOdbcRules; _dictionary = new Dictionary (StringComparer.InvariantCultureIgnoreCase); } #endregion // Constructors #region Properties [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)] [EditorBrowsable (EditorBrowsableState.Never)] [Browsable (false)] [DesignOnly (true)] public bool BrowsableConnectionString { get { throw new NotImplementedException (); } set { throw new NotImplementedException (); } } [RefreshProperties (RefreshProperties.All)] public string ConnectionString { get { IDictionary dictionary = (IDictionary ) _dictionary; StringBuilder sb = new StringBuilder (); foreach (string key in Keys) { object value = null; if (!dictionary.TryGetValue (key, out value)) continue; string val = value.ToString (); AppendKeyValuePair (sb, key, val, useOdbcRules); } return sb.ToString (); } set { Clear (); if (value == null) return; if (value.Trim ().Length == 0) return; ParseConnectionString (value); } } [Browsable (false)] public virtual int Count { get { return _dictionary.Count; } } [Browsable (false)] public virtual bool IsFixedSize { get { return false; } } [Browsable (false)] public bool IsReadOnly { get { throw new NotImplementedException (); } } [Browsable (false)] public virtual object this [string keyword] { get { if (ContainsKey (keyword)) return _dictionary [keyword]; else throw new ArgumentException (string.Format ( "Keyword '{0}' does not exist", keyword)); } set { if (value == null) { Remove (keyword); return; } if (keyword == null) throw new ArgumentNullException ("keyword"); if (keyword.Length == 0) throw CreateInvalidKeywordException (keyword); for (int i = 0; i < keyword.Length; i++) { char c = keyword [i]; if (i == 0 && (Char.IsWhiteSpace (c) || c == ';')) throw CreateInvalidKeywordException (keyword); if (i == (keyword.Length - 1) && Char.IsWhiteSpace (c)) throw CreateInvalidKeywordException (keyword); if (Char.IsControl (c)) throw CreateInvalidKeywordException (keyword); } if (ContainsKey (keyword)) _dictionary [keyword] = value; else _dictionary.Add (keyword, value); } } [Browsable (false)] public virtual ICollection Keys { get { string [] keys = new string [_dictionary.Keys.Count]; ((ICollection) _dictionary.Keys).CopyTo (keys, 0); ReadOnlyCollection keyColl = new ReadOnlyCollection (keys); return keyColl; } } bool ICollection.IsSynchronized { get { throw new NotImplementedException (); } } object ICollection.SyncRoot { get { throw new NotImplementedException (); } } object IDictionary.this [object keyword] { get { return this [(string) keyword]; } set { this [(string) keyword] = value; } } [Browsable (false)] public virtual ICollection Values { get { object [] values = new object [_dictionary.Values.Count]; ((ICollection) _dictionary.Values).CopyTo (values, 0); ReadOnlyCollection valuesColl = new ReadOnlyCollection (values); return valuesColl; } } #endregion // Properties #region Methods public void Add (string keyword, object value) { this [keyword] = value; } public static void AppendKeyValuePair (StringBuilder builder, string keyword, string value, bool useOdbcRules) { if (builder == null) throw new ArgumentNullException ("builder"); if (keyword == null) throw new ArgumentNullException ("keyName"); if (keyword.Length == 0) throw new ArgumentException ("Empty keyword is not valid."); if (builder.Length > 0) builder.Append (';'); if (!useOdbcRules) builder.Append (keyword.Replace ("=", "==")); else builder.Append (keyword); builder.Append ('='); if (value == null || value.Length == 0) return; if (!useOdbcRules) { bool dquoteFound = (value.IndexOf ('\"') > -1); bool squoteFound = (value.IndexOf ('\'') > -1); if (dquoteFound && squoteFound) { builder.Append ('\"'); builder.Append (value.Replace ("\"", "\"\"")); builder.Append ('\"'); } else if (dquoteFound) { builder.Append ('\''); builder.Append (value); builder.Append ('\''); } else if (squoteFound || value.IndexOf ('=') > -1 || value.IndexOf (';') > -1) { builder.Append ('\"'); builder.Append (value); builder.Append ('\"'); } else if (ValueNeedsQuoting (value)) { builder.Append ('\"'); builder.Append (value); builder.Append ('\"'); } else builder.Append (value); } else { int braces = 0; bool semicolonFound = false; int len = value.Length; bool needBraces = false; int lastChar = -1; for (int i = 0; i < len; i++) { int peek = 0; if (i == (len - 1)) peek = -1; else peek = value [i + 1]; char c = value [i]; switch (c) { case '{': braces++; break; case '}': if (peek.Equals (c)) { i++; continue; } else { braces--; if (peek != -1) needBraces = true; } break; case ';': semicolonFound = true; break; default: break; } lastChar = c; } if (value [0] == '{' && (lastChar != '}' || (braces == 0 && needBraces))) { builder.Append ('{'); builder.Append (value.Replace ("}", "}}")); builder.Append ('}'); return; } bool isDriver = (string.Compare (keyword, "Driver", StringComparison.InvariantCultureIgnoreCase) == 0); if (isDriver) { if (value [0] == '{' && lastChar == '}' && !needBraces) { builder.Append (value); return; } builder.Append ('{'); builder.Append (value.Replace ("}", "}}")); builder.Append ('}'); return; } if (value [0] == '{' && (braces != 0 || lastChar != '}') && needBraces) { builder.Append ('{'); builder.Append (value.Replace ("}", "}}")); builder.Append ('}'); return; } if (value [0] != '{' && semicolonFound) { builder.Append ('{'); builder.Append (value.Replace ("}", "}}")); builder.Append ('}'); return; } builder.Append (value); } } public static void AppendKeyValuePair (StringBuilder builder, string keyword, string value) { AppendKeyValuePair (builder, keyword, value, false); } public virtual void Clear () { _dictionary.Clear (); } public virtual bool ContainsKey (string keyword) { if (keyword == null) throw new ArgumentNullException ("keyword"); return _dictionary.ContainsKey (keyword); } public virtual bool EquivalentTo (DbConnectionStringBuilder connectionStringBuilder) { bool ret = true; try { if (Count != connectionStringBuilder.Count) ret = false; else { foreach (string key in Keys) { if (!this [key].Equals (connectionStringBuilder [key])) { ret = false; break; } } } } catch (ArgumentException) { ret = false; } return ret; } [MonoTODO] protected virtual void GetProperties (Hashtable propertyDescriptors) { throw new NotImplementedException (); } [MonoTODO] protected internal void ClearPropertyDescriptors () { throw new NotImplementedException (); } public virtual bool Remove (string keyword) { if (keyword == null) throw new ArgumentNullException ("keyword"); return _dictionary.Remove (keyword); } public virtual bool ShouldSerialize (string keyword) { throw new NotImplementedException (); } void ICollection.CopyTo (Array array, int index) { if (array == null) throw new ArgumentNullException ("array"); KeyValuePair [] arr = array as KeyValuePair []; if (arr == null) throw new ArgumentException ("Target array type is not compatible with the type of items in the collection"); ((ICollection>) _dictionary).CopyTo (arr, index); } void IDictionary.Add (object keyword, object value) { this.Add ((string) keyword, value); } bool IDictionary.Contains (object keyword) { return ContainsKey ((string) keyword); } IDictionaryEnumerator IDictionary.GetEnumerator () { return (IDictionaryEnumerator) _dictionary.GetEnumerator (); } void IDictionary.Remove (object keyword) { Remove ((string) keyword); } IEnumerator IEnumerable.GetEnumerator () { return (IEnumerator) _dictionary.GetEnumerator (); } private static object _staticAttributeCollection = null; AttributeCollection ICustomTypeDescriptor.GetAttributes () { object value = _staticAttributeCollection; if (value == null) { CLSCompliantAttribute clsAttr = new CLSCompliantAttribute (true); DefaultMemberAttribute defMemAttr = new DefaultMemberAttribute ("Item"); Attribute [] attrs = {clsAttr, defMemAttr}; value = new AttributeCollection (attrs); } System.Threading.Interlocked.CompareExchange (ref _staticAttributeCollection, value, null); return _staticAttributeCollection as AttributeCollection; } string ICustomTypeDescriptor.GetClassName () { return this.GetType ().ToString (); } string ICustomTypeDescriptor.GetComponentName () { return null; } TypeConverter ICustomTypeDescriptor.GetConverter () { return new CollectionConverter (); } EventDescriptor ICustomTypeDescriptor.GetDefaultEvent () { return null; } PropertyDescriptor ICustomTypeDescriptor.GetDefaultProperty () { return null; } object ICustomTypeDescriptor.GetEditor (Type editorBaseType) { return null; } EventDescriptorCollection ICustomTypeDescriptor.GetEvents () { return EventDescriptorCollection.Empty; } EventDescriptorCollection ICustomTypeDescriptor.GetEvents (Attribute [] attributes) { return EventDescriptorCollection.Empty; } PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties () { return PropertyDescriptorCollection.Empty; } PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties (Attribute [] attributes) { return PropertyDescriptorCollection.Empty; } object ICustomTypeDescriptor.GetPropertyOwner (PropertyDescriptor pd) { throw new NotImplementedException (); } public override string ToString () { return ConnectionString; } public virtual bool TryGetValue (string keyword, out object value) { // FIXME : not sure, difference between this [keyword] and this method bool found = ContainsKey (keyword); if (found) value = this [keyword]; else value = null; return found; } static ArgumentException CreateInvalidKeywordException (string keyword) { return new ArgumentException ("A keyword cannot contain " + "control characters, leading semicolons or " + "leading or trailing whitespace.", keyword); } static ArgumentException CreateConnectionStringInvalidException (int index) { return new ArgumentException ("Format of initialization " + "string does not conform to specifications at " + "index " + index + "."); } static bool ValueNeedsQuoting (string value) { foreach (char c in value) { if (char.IsWhiteSpace (c)) return true; } return false; } void ParseConnectionString (string connectionString) { if (useOdbcRules) ParseConnectionStringOdbc (connectionString); else ParseConnectionStringNonOdbc (connectionString); } void ParseConnectionStringOdbc (string connectionString) { bool inQuote = false; bool inDQuote = false; bool inName = true; bool inBraces = false; string name = String.Empty; string val = String.Empty; StringBuilder sb = new StringBuilder (); int len = connectionString.Length; for (int i = 0; i < len; i++) { char c = connectionString [i]; int peek = (i == (len - 1)) ? -1 : connectionString [i + 1]; switch (c) { case '{': if (inName) { sb.Append (c); continue; } if (sb.Length == 0) inBraces = true; sb.Append (c); break; case '}': if (inName || !inBraces) { sb.Append (c); continue; } if (peek == -1) { sb.Append (c); inBraces = false; } else if (peek.Equals (c)) { sb.Append (c); sb.Append (c); i++; } else { int next = NextNonWhitespaceChar (connectionString, i); if (next != -1 && ((char) next) != ';') throw CreateConnectionStringInvalidException (next); sb.Append (c); inBraces = false; } break; case ';': if (inName || inBraces) { sb.Append (c); continue; } if (name.Length > 0 && sb.Length > 0) { val = sb.ToString (); name = name.ToLower ().TrimEnd (); this [name] = val; } else if (sb.Length > 0) throw CreateConnectionStringInvalidException (c); inName = true; name = String.Empty; sb.Length = 0; break; case '=': if (inBraces || !inName) { sb.Append (c); continue; } name = sb.ToString (); if (name.Length == 0) throw CreateConnectionStringInvalidException (c); sb.Length = 0; inName = false; break; default: if (inDQuote || inQuote || inBraces) sb.Append (c); else if (char.IsWhiteSpace (c)) { // ignore leading whitespace if (sb.Length > 0) { int nextChar = SkipTrailingWhitespace (connectionString, i); if (nextChar == -1) sb.Append (c); else i = nextChar; } } else sb.Append (c); break; } } if ((inName && sb.Length > 0) || inDQuote || inQuote || inBraces) throw CreateConnectionStringInvalidException (len - 1); if (name.Length > 0 && sb.Length > 0) { val = sb.ToString (); name = name.ToLower ().TrimEnd (); this [name] = val; } } void ParseConnectionStringNonOdbc (string connectionString) { bool inQuote = false; bool inDQuote = false; bool inName = true; string name = String.Empty; string val = String.Empty; StringBuilder sb = new StringBuilder (); int len = connectionString.Length; for (int i = 0; i < len; i++) { char c = connectionString [i]; int peek = (i == (len - 1)) ? -1 : connectionString [i + 1]; switch (c) { case '\'': if (inName) { sb.Append (c); continue; } if (inDQuote) sb.Append (c); else if (inQuote) { if (peek == -1) inQuote = false; else if (peek.Equals (c)) { sb.Append (c); i++; } else { int next = NextNonWhitespaceChar (connectionString, i); if (next != -1 && ((char) next) != ';') throw CreateConnectionStringInvalidException (next); inQuote = false; } if (!inQuote) { val = sb.ToString (); name = name.ToLower ().TrimEnd (); this [name] = val; inName = true; name = String.Empty; sb.Length = 0; } } else if (sb.Length == 0) inQuote = true; else sb.Append (c); break; case '"': if (inName) { sb.Append (c); continue; } if (inQuote) sb.Append (c); else if (inDQuote) { if (peek == -1) inDQuote = false; else if (peek.Equals (c)) { sb.Append (c); i++; } else { int next = NextNonWhitespaceChar (connectionString, i); if (next != -1 && ((char) next) != ';') throw CreateConnectionStringInvalidException (next); inDQuote = false; } } else if (sb.Length == 0) inDQuote = true; else sb.Append (c); break; case ';': if (inName) { sb.Append (c); continue; } if (inDQuote || inQuote) sb.Append (c); else { if (name.Length > 0 && sb.Length > 0) { val = sb.ToString (); name = name.ToLower ().TrimEnd (); this [name] = val; } else if (sb.Length > 0) throw CreateConnectionStringInvalidException (c); inName = true; name = String.Empty; sb.Length = 0; } break; case '=': if (inDQuote || inQuote || !inName) sb.Append (c); else if (peek != -1 && peek.Equals (c)) { sb.Append (c); i++; } else { name = sb.ToString (); if (name.Length == 0) throw CreateConnectionStringInvalidException (c); sb.Length = 0; inName = false; } break; default: if (inDQuote || inQuote) sb.Append (c); else if (char.IsWhiteSpace (c)) { // ignore leading whitespace if (sb.Length > 0) { int nextChar = SkipTrailingWhitespace (connectionString, i); if (nextChar == -1) sb.Append (c); else i = nextChar; } } else sb.Append (c); break; } } if ((inName && sb.Length > 0) || inDQuote || inQuote) throw CreateConnectionStringInvalidException (len -1); if (name.Length > 0 && sb.Length > 0) { val = sb.ToString (); name = name.ToLower ().TrimEnd (); this [name] = val; } } static int SkipTrailingWhitespace (string value, int index) { int len = value.Length; for (int i = (index + 1); i < len; i++) { char c = value [i]; if (c == ';') return (i - 1); if (!char.IsWhiteSpace (c)) return -1; } return len - 1; } static int NextNonWhitespaceChar (string value, int index) { int len = value.Length; for (int i = (index + 1); i < len; i++) { char c = value [i]; if (!char.IsWhiteSpace (c)) return (int) c; } return -1; } #endregion // Public Methods } } #endif // NET_2_0 using