2009-08-20 Sebastien Pouliot <sebastien@ximian.com>
[mono.git] / mcs / class / System / System.Collections.Specialized / NameValueCollection.cs
1 //
2 // System.Collections.Specialized.NameValueCollection.cs
3 //
4 // Author:
5 //   Gleb Novodran
6 //
7 // (C) Ximian, Inc.  http://www.ximian.com
8 // Copyright (C) 2004-2005 Novell (http://www.novell.com)
9 //
10
11 //
12 // Permission is hereby granted, free of charge, to any person obtaining
13 // a copy of this software and associated documentation files (the
14 // "Software"), to deal in the Software without restriction, including
15 // without limitation the rights to use, copy, modify, merge, publish,
16 // distribute, sublicense, and/or sell copies of the Software, and to
17 // permit persons to whom the Software is furnished to do so, subject to
18 // the following conditions:
19 // 
20 // The above copyright notice and this permission notice shall be
21 // included in all copies or substantial portions of the Software.
22 // 
23 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
24 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
25 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
26 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
27 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
28 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
29 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
30 //
31
32 using System.Runtime.Serialization;
33 using System.Text;
34
35 namespace System.Collections.Specialized{
36
37         [Serializable]
38         public class NameValueCollection : NameObjectCollectionBase
39         {
40                 string[] cachedAllKeys = null;
41                 string[] cachedAll = null;
42
43                 //--------------------- Constructors -----------------------------
44
45                 public NameValueCollection () : base ()
46                 {
47                 }
48                 
49                 public NameValueCollection (int capacity) : base (capacity)
50                 {
51                 }
52 #if NET_2_0
53         public NameValueCollection (NameValueCollection col) : base (( col == null ) ? null : col.EqualityComparer ,
54                                                                 (col == null) ? null : col.Comparer, 
55                                                                 (col == null) ? null : col.HashCodeProvider)
56         {
57             if (col==null)
58                 throw new ArgumentNullException ("col");                
59             Add(col);
60         }
61 #else
62                 public NameValueCollection (NameValueCollection col) : base ((col == null) ? null : col.HashCodeProvider ,
63                                                                                                                                 (col == null) ? null : col.Comparer)                                                                                                                            
64                 {
65                         if (col==null)
66                                 throw new NullReferenceException ();            
67                         Add(col);
68                 }
69 #endif
70
71 #if NET_2_0
72                 [Obsolete ("Use NameValueCollection (IEqualityComparer)")]
73 #endif
74                 public NameValueCollection (IHashCodeProvider hashProvider, IComparer comparer)
75                         : base (hashProvider, comparer)
76                 {
77                         
78                 }
79
80                 public NameValueCollection (int capacity, NameValueCollection col)
81                         : base (capacity, (col == null) ? null : col.HashCodeProvider, 
82                                         (col == null) ? null : col.Comparer)
83                 {
84                         Add (col);                      
85                 }
86
87                 protected NameValueCollection (SerializationInfo info, StreamingContext context)
88                         :base (info, context)
89                 {
90                         
91                 }
92                 
93 #if NET_2_0
94                 [Obsolete ("Use NameValueCollection (IEqualityComparer)")]
95 #endif
96                 public NameValueCollection (int capacity, IHashCodeProvider hashProvider, IComparer comparer)
97                         :base (capacity, hashProvider, comparer)
98                 {
99                         
100                 }
101
102 #if NET_2_0
103                 public NameValueCollection (IEqualityComparer equalityComparer)
104                         : base (equalityComparer)
105                 {
106                 }
107
108                 public NameValueCollection (int capacity, IEqualityComparer equalityComparer)
109                         : base (capacity, equalityComparer)
110                 {
111                 }
112 #endif
113
114                 public virtual string[] AllKeys 
115                 {
116                         get {
117                                 if (cachedAllKeys == null)
118                                         cachedAllKeys = BaseGetAllKeys ();
119                                 return this.cachedAllKeys;
120                         }
121                 }
122                 
123                 public string this [int index] 
124                 {
125                         get{
126                                 return this.Get (index);
127                         }
128                 }
129                 
130                 public string this [string name] {
131                         get{
132                                 return this.Get (name);
133                         }
134                         set{
135                                 this.Set (name,value);
136                         }
137                 }
138                 
139                 public void Add (NameValueCollection c)
140                 {
141                         if (this.IsReadOnly)
142                                 throw new NotSupportedException ("Collection is read-only");
143 #if NET_2_0
144                         if (c == null)
145                                 throw new ArgumentNullException ("c");
146 #endif
147 // make sense - but it's not the exception thrown
148 //                              throw new ArgumentNullException ();
149                         
150                         InvalidateCachedArrays ();
151                         int max = c.Count;
152                         for (int i=0; i < max; i++){
153                                 string key = c.GetKey (i);
154                                 ArrayList new_values = (ArrayList) c.BaseGet (i);
155                                 ArrayList values = (ArrayList) BaseGet (key);
156                                 if (values != null && new_values != null)
157                                         values.AddRange (new_values);
158                                 else if (new_values != null)
159                                         values = new ArrayList (new_values);
160                                 BaseSet (key, values);
161                         }
162                 }
163
164                 /// in SDK doc: If the same value already exists under the same key in the collection, 
165                 /// it just adds one more value in other words after
166                 /// <code>
167                 /// NameValueCollection nvc;
168                 /// nvc.Add ("LAZY","BASTARD")
169                 /// nvc.Add ("LAZY","BASTARD")
170                 /// </code>
171                 /// nvc.Get ("LAZY") will be "BASTARD,BASTARD" instead of "BASTARD"
172
173                 public virtual void Add (string name, string val)
174                 {
175                         if (this.IsReadOnly)
176                                 throw new NotSupportedException ("Collection is read-only");
177                         
178                         InvalidateCachedArrays ();
179                         ArrayList values = (ArrayList)BaseGet (name);
180                         if (values == null){
181                                 values = new ArrayList ();
182                                 if (val != null)
183                                         values.Add (val);
184                                 BaseAdd (name, values);
185                         }
186                         else {
187                                 if (val != null)
188                                         values.Add (val);
189                         }
190
191                 }
192
193 #if NET_2_0
194                 public virtual void Clear ()
195 #else
196                 public void Clear ()
197 #endif
198                 {
199                         if (this.IsReadOnly)
200                                 throw new NotSupportedException ("Collection is read-only");
201                         InvalidateCachedArrays ();
202                         BaseClear ();
203                 }
204
205                 public void CopyTo (Array dest, int index)
206                 {
207                         if (dest == null)
208                                 throw new ArgumentNullException ("dest", "Null argument - dest");
209                         if (index < 0)
210                                 throw new ArgumentOutOfRangeException ("index", "index is less than 0");
211                         if (dest.Rank > 1)
212                                 throw new ArgumentException ("dest", "multidim");
213
214                         if (cachedAll == null)
215                                 RefreshCachedAll ();
216 #if NET_2_0
217                         try {
218 #endif                  
219                         cachedAll.CopyTo (dest, index);
220 #if NET_2_0
221                         }
222                         catch (ArrayTypeMismatchException)  
223                         {
224                                 throw new InvalidCastException();
225                         }
226 #endif                  
227                 }
228
229                 private void RefreshCachedAll ()
230                 {
231                         this.cachedAll = null;
232                         int max = this.Count;
233                         cachedAll = new string [max];
234                         for (int i = 0; i < max; i++)
235                                 cachedAll [i] = this.Get (i);
236                 }
237                 
238                 public virtual string Get (int index)
239                 {
240                         ArrayList values = (ArrayList)BaseGet (index);
241                         // if index is out of range BaseGet throws an ArgumentOutOfRangeException
242
243                         return AsSingleString (values);
244                 }
245                 
246                 public virtual string Get (string name)
247                 {
248                         ArrayList values = (ArrayList)BaseGet (name);
249                         return AsSingleString (values);
250                 }
251
252                 private static string AsSingleString (ArrayList values)
253                 {
254                         const char separator = ',';
255                         
256                         if (values == null)
257                                 return null;
258                         int max = values.Count;
259                         
260                         switch (max) {
261                         case 0:
262                                 return null;
263                         case 1:
264                                 return (string)values [0];
265                         case 2:
266                                 return String.Concat ((string)values [0], separator, (string)values [1]);
267                         default:
268                                 int len = max;
269                                 for (int i = 0; i < max; i++)
270                                         len += ((string)values [i]).Length;
271                                 StringBuilder sb = new StringBuilder ((string)values [0], len);
272                                 for (int i = 1; i < max; i++){
273                                         sb.Append (separator);
274                                         sb.Append (values [i]);
275                                 }
276
277                                 return sb.ToString ();
278                         }
279                 }
280                 
281                 
282                 public virtual string GetKey (int index)
283                 {
284                         return BaseGetKey (index);
285                 }
286                 
287                 
288                 public virtual string[] GetValues (int index)
289                 {
290                         ArrayList values = (ArrayList)BaseGet (index);
291                         
292                         return AsStringArray (values);
293                 }
294                 
295                 
296                 public virtual string[] GetValues (string name)
297                 {
298                         ArrayList values = (ArrayList)BaseGet (name);
299                         
300                         return AsStringArray (values);
301                 }
302                 
303                 private static string[] AsStringArray (ArrayList values)
304                 {
305                         if (values == null)
306                                 return null;
307                         int max = values.Count;//get_Count ();
308                         if (max == 0)
309                                 return null;
310                         
311                         string[] valArray = new string[max];
312                         values.CopyTo (valArray);
313                         return valArray;
314                 }
315                 
316                 public bool HasKeys ()
317                 {
318                         return BaseHasKeys ();
319                 }
320                 
321                 public virtual void Remove (string name)
322                 {
323                         if (this.IsReadOnly)
324                                 throw new NotSupportedException ("Collection is read-only");
325                         InvalidateCachedArrays ();
326                         BaseRemove (name);
327                         
328                 }
329                 
330                 public virtual void Set (string name, string value)
331                 {
332                         if (this.IsReadOnly)
333                                 throw new NotSupportedException ("Collection is read-only");
334
335                         InvalidateCachedArrays ();
336                         
337                         ArrayList values = new ArrayList ();
338                         if (value != null) {
339                                 values.Add (value);
340                                 BaseSet (name,values);
341                         }
342                         else {
343                                 // remove all entries
344                                 BaseSet (name, null);
345                         }
346                 }
347                 
348                 protected void InvalidateCachedArrays ()
349                 {
350                         cachedAllKeys = null;
351                         cachedAll = null;
352                 }
353
354         }
355 }