2 // System.Data.Common.DbConnectionStringBuilder.cs
5 // Sureshkumar T (tsureshkumar@novell.com)
6 // Gert Driesen (drieseng@users.sourceforge.net
8 // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
10 // Permission is hereby granted, free of charge, to any person obtaining
11 // a copy of this software and associated documentation files (the
12 // "Software"), to deal in the Software without restriction, including
13 // without limitation the rights to use, copy, modify, merge, publish,
14 // distribute, sublicense, and/or sell copies of the Software, and to
15 // permit persons to whom the Software is furnished to do so, subject to
16 // the following conditions:
18 // The above copyright notice and this permission notice shall be
19 // included in all copies or substantial portions of the Software.
21 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
22 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
23 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
24 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
25 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
26 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
27 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
32 using System.Collections;
33 using System.Collections.Generic;
34 using System.Collections.ObjectModel;
35 using System.ComponentModel;
37 using System.Data.Common;
38 using System.Reflection;
41 namespace System.Data.Common
43 public class DbConnectionStringBuilder : IDictionary, ICollection, IEnumerable, ICustomTypeDescriptor
47 readonly Dictionary<string, object> _dictionary;
48 readonly bool useOdbcRules;
54 public DbConnectionStringBuilder () : this (false)
58 public DbConnectionStringBuilder (bool useOdbcRules)
60 this.useOdbcRules = useOdbcRules;
61 _dictionary = new Dictionary <string, object> (StringComparer.InvariantCultureIgnoreCase);
64 #endregion // Constructors
68 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
69 [EditorBrowsable (EditorBrowsableState.Never)]
72 public bool BrowsableConnectionString {
73 get { throw new NotImplementedException (); }
74 set { throw new NotImplementedException (); }
77 [RefreshProperties (RefreshProperties.All)]
78 public string ConnectionString {
80 IDictionary<string, object> dictionary = (IDictionary <string, object>) _dictionary;
81 StringBuilder sb = new StringBuilder ();
82 foreach (string key in Keys) {
84 if (!dictionary.TryGetValue (key, out value))
86 string val = value.ToString ();
87 AppendKeyValuePair (sb, key, val, useOdbcRules);
89 return sb.ToString ();
95 if (value.Trim ().Length == 0)
97 ParseConnectionString (value);
102 public virtual int Count
104 get { return _dictionary.Count; }
108 public virtual bool IsFixedSize
110 get { return false; }
114 public bool IsReadOnly
116 get { throw new NotImplementedException (); }
120 public virtual object this [string keyword] {
122 if (ContainsKey (keyword))
123 return _dictionary [keyword];
125 throw new ArgumentException (string.Format (
126 "Keyword '{0}' does not exist",
136 throw new ArgumentNullException ("keyword");
138 if (keyword.Length == 0)
139 throw CreateInvalidKeywordException (keyword);
141 for (int i = 0; i < keyword.Length; i++) {
142 char c = keyword [i];
143 if (i == 0 && (Char.IsWhiteSpace (c) || c == ';'))
144 throw CreateInvalidKeywordException (keyword);
145 if (i == (keyword.Length - 1) && Char.IsWhiteSpace (c))
146 throw CreateInvalidKeywordException (keyword);
147 if (Char.IsControl (c))
148 throw CreateInvalidKeywordException (keyword);
151 if (ContainsKey (keyword))
152 _dictionary [keyword] = value;
154 _dictionary.Add (keyword, value);
159 public virtual ICollection Keys
162 string [] keys = new string [_dictionary.Keys.Count];
163 ((ICollection<string>) _dictionary.Keys).CopyTo (keys, 0);
164 ReadOnlyCollection<string> keyColl = new ReadOnlyCollection<string> (keys);
169 bool ICollection.IsSynchronized
171 get { throw new NotImplementedException (); }
174 object ICollection.SyncRoot
176 get { throw new NotImplementedException (); }
179 object IDictionary.this [object keyword]
181 get { return this [(string) keyword]; }
182 set { this [(string) keyword] = value; }
186 public virtual ICollection Values {
188 object [] values = new object [_dictionary.Values.Count];
189 ((ICollection<object>) _dictionary.Values).CopyTo (values, 0);
190 ReadOnlyCollection<object> valuesColl = new ReadOnlyCollection<object> (values);
195 #endregion // Properties
199 public void Add (string keyword, object value)
201 this [keyword] = value;
204 public static void AppendKeyValuePair (StringBuilder builder, string keyword, string value,
208 throw new ArgumentNullException ("builder");
210 throw new ArgumentNullException ("keyName");
211 if (keyword.Length == 0)
212 throw new ArgumentException ("Empty keyword is not valid.");
214 if (builder.Length > 0)
215 builder.Append (';');
217 builder.Append (keyword.Replace ("=", "=="));
219 builder.Append (keyword);
220 builder.Append ('=');
222 if (value == null || value.Length == 0)
226 bool dquoteFound = (value.IndexOf ('\"') > -1);
227 bool squoteFound = (value.IndexOf ('\'') > -1);
229 if (dquoteFound && squoteFound) {
230 builder.Append ('\"');
231 builder.Append (value.Replace ("\"", "\"\""));
232 builder.Append ('\"');
233 } else if (dquoteFound) {
234 builder.Append ('\'');
235 builder.Append (value);
236 builder.Append ('\'');
237 } else if (squoteFound || value.IndexOf ('=') > -1 || value.IndexOf (';') > -1) {
238 builder.Append ('\"');
239 builder.Append (value);
240 builder.Append ('\"');
241 } else if (ValueNeedsQuoting (value)) {
242 builder.Append ('\"');
243 builder.Append (value);
244 builder.Append ('\"');
246 builder.Append (value);
249 bool semicolonFound = false;
250 int len = value.Length;
251 bool needBraces = false;
255 for (int i = 0; i < len; i++) {
260 peek = value [i + 1];
268 if (peek.Equals (c)) {
278 semicolonFound = true;
286 if (value [0] == '{' && (lastChar != '}' || (braces == 0 && needBraces))) {
287 builder.Append ('{');
288 builder.Append (value.Replace ("}", "}}"));
289 builder.Append ('}');
293 bool isDriver = (string.Compare (keyword, "Driver", StringComparison.InvariantCultureIgnoreCase) == 0);
295 if (value [0] == '{' && lastChar == '}' && !needBraces) {
296 builder.Append (value);
299 builder.Append ('{');
300 builder.Append (value.Replace ("}", "}}"));
301 builder.Append ('}');
305 if (value [0] == '{' && (braces != 0 || lastChar != '}') && needBraces) {
306 builder.Append ('{');
307 builder.Append (value.Replace ("}", "}}"));
308 builder.Append ('}');
312 if (value [0] != '{' && semicolonFound) {
313 builder.Append ('{');
314 builder.Append (value.Replace ("}", "}}"));
315 builder.Append ('}');
319 builder.Append (value);
323 public static void AppendKeyValuePair (StringBuilder builder, string keyword, string value)
325 AppendKeyValuePair (builder, keyword, value, false);
328 public virtual void Clear ()
330 _dictionary.Clear ();
333 public virtual bool ContainsKey (string keyword)
336 throw new ArgumentNullException ("keyword");
337 return _dictionary.ContainsKey (keyword);
340 public virtual bool EquivalentTo (DbConnectionStringBuilder connectionStringBuilder)
344 if (Count != connectionStringBuilder.Count)
347 foreach (string key in Keys) {
348 if (!this [key].Equals (connectionStringBuilder [key])) {
354 } catch (ArgumentException) {
361 protected virtual void GetProperties (Hashtable propertyDescriptors)
363 throw new NotImplementedException ();
367 protected internal void ClearPropertyDescriptors ()
369 throw new NotImplementedException ();
372 public virtual bool Remove (string keyword)
375 throw new ArgumentNullException ("keyword");
376 return _dictionary.Remove (keyword);
379 public virtual bool ShouldSerialize (string keyword)
381 throw new NotImplementedException ();
384 void ICollection.CopyTo (Array array, int index)
387 throw new ArgumentNullException ("array");
388 KeyValuePair<string, object> [] arr = array as KeyValuePair<string, object> [];
390 throw new ArgumentException ("Target array type is not compatible with the type of items in the collection");
391 ((ICollection<KeyValuePair<string, object>>) _dictionary).CopyTo (arr, index);
394 void IDictionary.Add (object keyword, object value)
396 this.Add ((string) keyword, value);
399 bool IDictionary.Contains (object keyword)
401 return ContainsKey ((string) keyword);
404 IDictionaryEnumerator IDictionary.GetEnumerator ()
406 return (IDictionaryEnumerator) _dictionary.GetEnumerator ();
409 void IDictionary.Remove (object keyword)
411 Remove ((string) keyword);
414 IEnumerator IEnumerable.GetEnumerator ()
416 return (IEnumerator) _dictionary.GetEnumerator ();
419 private static object _staticAttributeCollection = null;
420 AttributeCollection ICustomTypeDescriptor.GetAttributes ()
422 object value = _staticAttributeCollection;
424 CLSCompliantAttribute clsAttr = new CLSCompliantAttribute (true);
425 DefaultMemberAttribute defMemAttr = new DefaultMemberAttribute ("Item");
426 Attribute [] attrs = {clsAttr, defMemAttr};
427 value = new AttributeCollection (attrs);
429 System.Threading.Interlocked.CompareExchange (ref _staticAttributeCollection, value, null);
430 return _staticAttributeCollection as AttributeCollection;
433 string ICustomTypeDescriptor.GetClassName ()
435 return this.GetType ().ToString ();
438 string ICustomTypeDescriptor.GetComponentName ()
443 TypeConverter ICustomTypeDescriptor.GetConverter ()
445 return new CollectionConverter ();
448 EventDescriptor ICustomTypeDescriptor.GetDefaultEvent ()
453 PropertyDescriptor ICustomTypeDescriptor.GetDefaultProperty ()
458 object ICustomTypeDescriptor.GetEditor (Type editorBaseType)
463 EventDescriptorCollection ICustomTypeDescriptor.GetEvents ()
465 return EventDescriptorCollection.Empty;
468 EventDescriptorCollection ICustomTypeDescriptor.GetEvents (Attribute [] attributes)
470 return EventDescriptorCollection.Empty;
473 PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties ()
475 return PropertyDescriptorCollection.Empty;
478 PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties (Attribute [] attributes)
480 return PropertyDescriptorCollection.Empty;
483 object ICustomTypeDescriptor.GetPropertyOwner (PropertyDescriptor pd)
485 throw new NotImplementedException ();
488 public override string ToString ()
490 return ConnectionString;
493 public virtual bool TryGetValue (string keyword, out object value)
495 // FIXME : not sure, difference between this [keyword] and this method
496 bool found = ContainsKey (keyword);
498 value = this [keyword];
504 static ArgumentException CreateInvalidKeywordException (string keyword)
506 return new ArgumentException ("A keyword cannot contain "
507 + "control characters, leading semicolons or "
508 + "leading or trailing whitespace.", keyword);
511 static ArgumentException CreateConnectionStringInvalidException (int index)
513 return new ArgumentException ("Format of initialization "
514 + "string does not conform to specifications at "
515 + "index " + index + ".");
518 static bool ValueNeedsQuoting (string value)
520 foreach (char c in value) {
521 if (char.IsWhiteSpace (c))
526 void ParseConnectionString (string connectionString)
529 ParseConnectionStringOdbc (connectionString);
531 ParseConnectionStringNonOdbc (connectionString);
534 void ParseConnectionStringOdbc (string connectionString)
536 bool inQuote = false;
537 bool inDQuote = false;
539 bool inBraces = false;
541 string name = String.Empty;
542 string val = String.Empty;
543 StringBuilder sb = new StringBuilder ();
544 int len = connectionString.Length;
546 for (int i = 0; i < len; i++) {
547 char c = connectionString [i];
548 int peek = (i == (len - 1)) ? -1 : connectionString [i + 1];
562 if (inName || !inBraces) {
570 } else if (peek.Equals (c)) {
575 int next = NextNonWhitespaceChar (connectionString, i);
576 if (next != -1 && ((char) next) != ';')
577 throw CreateConnectionStringInvalidException (next);
583 if (inName || inBraces) {
588 if (name.Length > 0 && sb.Length > 0) {
589 val = sb.ToString ();
590 name = name.ToLower ().TrimEnd ();
592 } else if (sb.Length > 0)
593 throw CreateConnectionStringInvalidException (c);
599 if (inBraces || !inName) {
604 name = sb.ToString ();
605 if (name.Length == 0)
606 throw CreateConnectionStringInvalidException (c);
611 if (inDQuote || inQuote || inBraces)
613 else if (char.IsWhiteSpace (c)) {
614 // ignore leading whitespace
616 int nextChar = SkipTrailingWhitespace (connectionString, i);
628 if ((inName && sb.Length > 0) || inDQuote || inQuote || inBraces)
629 throw CreateConnectionStringInvalidException (len - 1);
631 if (name.Length > 0 && sb.Length > 0) {
632 val = sb.ToString ();
633 name = name.ToLower ().TrimEnd ();
638 void ParseConnectionStringNonOdbc (string connectionString)
640 bool inQuote = false;
641 bool inDQuote = false;
644 string name = String.Empty;
645 string val = String.Empty;
646 StringBuilder sb = new StringBuilder ();
647 int len = connectionString.Length;
649 for (int i = 0; i < len; i++) {
650 char c = connectionString [i];
651 int peek = (i == (len - 1)) ? -1 : connectionString [i + 1];
665 else if (peek.Equals (c)) {
669 int next = NextNonWhitespaceChar (connectionString, i);
670 if (next != -1 && ((char) next) != ';')
671 throw CreateConnectionStringInvalidException (next);
676 val = sb.ToString ();
677 name = name.ToLower ().TrimEnd ();
683 } else if (sb.Length == 0)
699 else if (peek.Equals (c)) {
703 int next = NextNonWhitespaceChar (connectionString, i);
704 if (next != -1 && ((char) next) != ';')
705 throw CreateConnectionStringInvalidException (next);
708 } else if (sb.Length == 0)
719 if (inDQuote || inQuote)
722 if (name.Length > 0 && sb.Length > 0) {
723 val = sb.ToString ();
724 name = name.ToLower ().TrimEnd ();
726 } else if (sb.Length > 0)
727 throw CreateConnectionStringInvalidException (c);
734 if (inDQuote || inQuote || !inName)
736 else if (peek != -1 && peek.Equals (c)) {
740 name = sb.ToString ();
741 if (name.Length == 0)
742 throw CreateConnectionStringInvalidException (c);
748 if (inDQuote || inQuote)
750 else if (char.IsWhiteSpace (c)) {
751 // ignore leading whitespace
753 int nextChar = SkipTrailingWhitespace (connectionString, i);
765 if ((inName && sb.Length > 0) || inDQuote || inQuote)
766 throw CreateConnectionStringInvalidException (len -1);
768 if (name.Length > 0 && sb.Length > 0) {
769 val = sb.ToString ();
770 name = name.ToLower ().TrimEnd ();
775 static int SkipTrailingWhitespace (string value, int index)
777 int len = value.Length;
778 for (int i = (index + 1); i < len; i++) {
782 if (!char.IsWhiteSpace (c))
788 static int NextNonWhitespaceChar (string value, int index)
790 int len = value.Length;
791 for (int i = (index + 1); i < len; i++) {
793 if (!char.IsWhiteSpace (c))
799 #endregion // Public Methods
802 #endif // NET_2_0 using