New test.
[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                 /// <summary> SDK: Initializes a new instance of the NameValueCollection class that is empty,
46                 /// has the default initial capacity and uses the default case-insensitive hash code provider and the default case-insensitive comparer.
47                 /// </summary>
48                 public NameValueCollection() : base()
49                 {
50                 }
51                 
52                 /// <summary> SDK: Initializes a new instance of the NameValueCollection class that is empty, 
53                 /// has the specified initial capacity and uses the default case-insensitive hash code provider and the default case-insensitive comparer.
54                 ///</summary>
55                 public NameValueCollection( int capacity ) : base(capacity)
56                 {
57                 }
58
59                 /// <summary> SDK: Copies the entries from the specified NameValueCollection to a new 
60                 /// NameValueCollection with the same initial capacity as the number of entries copied 
61                 /// and using the same case-insensitive hash code provider and the same case-insensitive 
62                 /// comparer as the source collection.
63                 /// </summary>
64                 /// TODO: uncomment constructor below after it will be possible to compile NameValueCollection and
65                 /// NameObjectCollectionBase to the same assembly 
66                 
67                 public NameValueCollection( NameValueCollection col ) : base(col.HashCodeProvider,col.Comparer)
68                 {
69                         if (col==null)
70                                 throw new ArgumentNullException ("col");
71                         Add(col);
72                 }
73
74                 ///<summary>SDK: Initializes a new instance of the NameValueCollection class that is empty, 
75                 ///has the default initial capacity and uses the specified hash code provider and 
76                 ///the specified comparer.</summary>
77 #if NET_2_0
78                 [Obsolete ("Use NameValueCollection(IEqualityComparer)")]
79 #endif
80                 public NameValueCollection( IHashCodeProvider hashProvider, IComparer comparer )
81                         : base(hashProvider, comparer)
82                 {
83                         
84                 }
85
86                 /// <summary>
87                 /// SDK: Copies the entries from the specified NameValueCollection to a new NameValueCollection
88                 /// with the specified initial capacity or the same initial capacity as the number of entries 
89                 /// copied, whichever is greater, and using the default case-insensitive hash code provider and 
90                 /// the default case-insensitive comparer.
91                 /// </summary>
92                 /// TODO: uncomment constructor below after it will be possible to compile NameValueCollection and
93                 /// NameObjectCollectionBase to the same assembly 
94                 
95                 public NameValueCollection( int capacity, NameValueCollection col )
96                         : base(capacity, col.HashCodeProvider, col.Comparer)
97                 {
98                         Add(col);                       
99                 }
100
101                 /// <summary>
102                 /// SDK: Initializes a new instance of the NameValueCollection class that is serializable
103                 /// and uses the specified System.Runtime.Serialization.SerializationInfo and 
104                 /// System.Runtime.Serialization.StreamingContext.
105                 /// </summary>
106                 protected NameValueCollection( SerializationInfo info, StreamingContext context )
107                         :base(info, context)
108                 {
109                         
110                 }
111                 
112                 /// <summary>
113                 /// SDK: Initializes a new instance of the NameValueCollection class that is empty, 
114                 /// has the specified initial capacity and uses the specified hash code provider and 
115                 /// the specified comparer.
116                 /// </summary>
117 #if NET_2_0
118                 [Obsolete ("Use NameValueCollection(IEqualityComparer)")]
119 #endif
120                 public NameValueCollection( int capacity, IHashCodeProvider hashProvider, IComparer comparer )
121                         :base(capacity, hashProvider, comparer)
122                 {
123                         
124                 }
125
126 #if NET_2_0
127                 public NameValueCollection (IEqualityComparer equalityComparer)
128                         : base (equalityComparer)
129                 {
130                 }
131
132                 public NameValueCollection (int capacity, IEqualityComparer equalityComparer)
133                         : base (capacity, equalityComparer)
134                 {
135                 }
136 #endif
137
138         //----------------------- Public Instance Properties -------------------------------
139
140                 
141                 ///<summary> SDK:
142                 /// Gets all the keys in the NameValueCollection.
143                 /// The arrays returned by AllKeys are cached for better performance and are 
144                 /// automatically refreshed when the collection changes. A derived class can 
145                 /// invalidate the cached version by calling InvalidateCachedArrays, thereby 
146                 /// forcing the arrays to be recreated.
147                 /// </summary>
148                 public virtual string[] AllKeys 
149                 {
150                         get {
151                                 if (cachedAllKeys==null)
152                                         cachedAllKeys = BaseGetAllKeys();
153                                 return this.cachedAllKeys;
154                         }
155                 }
156                 
157                 public string this[ int index ] 
158                 {
159                         get{
160                                 return this.Get(index);
161                         }
162                 }
163                 
164                 public string this[ string name ] {
165                         get{
166                                 return this.Get(name);
167                         }
168                         set{
169                                 this.Set(name,value);
170                         }
171                 }
172                 
173 /////////////////////////////// Public Instance Methods //////////////////////////////
174                 
175                 /// <summary> SDK: Copies the entries in the specified NameValueCollection 
176                 /// to the current NameValueCollection.</summary>
177                 /// LAMESPEC: see description that comes this Add(string, string)
178                 
179                 public void Add (NameValueCollection c)
180                 {
181                         if (this.IsReadOnly)
182                                 throw new NotSupportedException ("Collection is read-only");
183 #if NET_2_0
184                         if (c == null)
185                                 throw new ArgumentNullException ("c");
186 #endif
187 // make sense - but it's not the exception thrown
188 //                              throw new ArgumentNullException ();
189                         
190                         InvalidateCachedArrays ();
191                         int max = c.Count;
192                         for (int i=0; i < max; i++){
193                                 string key = c.GetKey (i);
194                                 ArrayList new_values = (ArrayList) c.BaseGet (i);
195                                 ArrayList values = (ArrayList) BaseGet (key);
196                                 if (values != null && new_values != null)
197                                         values.AddRange (new_values);
198                                 else if (new_values != null)
199                                         values = new ArrayList (new_values);
200                                 BaseSet (key, values);
201                         }
202                 }
203
204                 
205                 /// <summary> SDK: Adds an entry with the specified name and value to the 
206                 /// NameValueCollection. </summary>
207                 /// 
208                 /// in SDK doc: If the same value already exists under the same key in the collection, 
209                 /// it just adds one more value in other words after
210                 /// <code>
211                 /// NameValueCollection nvc;
212                 /// nvc.Add("LAZY","BASTARD")
213                 /// nvc.Add("LAZY","BASTARD")
214                 /// </code>
215                 /// nvc.Get("LAZY") will be "BASTARD,BASTARD" instead of "BASTARD"
216
217                 public virtual void Add( string name, string val )
218                 {
219                         
220                         if (this.IsReadOnly)
221                                 throw new NotSupportedException("Collection is read-only");
222                         
223                         InvalidateCachedArrays();
224                         ArrayList values = (ArrayList)BaseGet(name);
225                         if (values==null){
226                                 values = new ArrayList();
227                                 if (val!=null)
228                                         values.Add(val);
229                                 BaseAdd(name,values);
230                         }
231                         else {
232                                 if (val!=null)
233                                         values.Add(val);
234                         }
235
236                 }
237
238                 /// <summary> SDK: Invalidates the cached arrays and removes all entries from 
239                 /// the NameValueCollection.</summary>
240 #if NET_2_0
241                 public virtual void Clear ()
242 #else
243                 public void Clear ()
244 #endif
245                 {
246                         if (this.IsReadOnly)
247                                 throw new NotSupportedException("Collection is read-only");
248                         InvalidateCachedArrays();
249                         BaseClear();
250                 }
251
252                 /// <summary> SDK: Copies the entire NameValueCollection to a compatible one-dimensional Array,
253                 /// starting at the specified index of the target array.</summary>
254
255                 public void CopyTo( Array dest, int index )
256                 {
257                         if (dest==null)
258                                 throw new ArgumentNullException("dest", "Null argument - dest");
259                         if (index<0)
260                                 throw new ArgumentOutOfRangeException("index", "index is less than 0");
261                         if (dest.Rank > 1)
262                                 throw new ArgumentException ("dest", "multidim");
263
264                         if (cachedAll==null)
265                                 RefreshCachedAll();
266                         cachedAll.CopyTo(dest, index);
267                 }
268
269                 private void RefreshCachedAll()
270                 {
271                         this.cachedAll=null;
272                         int max = this.Count;
273                         cachedAll = new string[max];
274                         for(int i=0;i<max;i++){
275                                 cachedAll[i] = this.Get(i);
276                         }
277                         
278                 }
279                 
280                 /// <summary> SDK: Gets the values at the specified index of the NameValueCollection combined
281                 /// into one comma-separated list.</summary>
282                 
283                 public virtual string Get( int index )
284                 {
285                         ArrayList values = (ArrayList)BaseGet(index);
286                         // if index is out of range BaseGet throws an ArgumentOutOfRangeException
287
288                         return AsSingleString(values);
289                         
290                 }
291                 
292                 /**
293                  * SDK: Gets the values associated with the specified key from the NameValueCollection
294                  * combined into one comma-separated list.
295                  */
296                 public virtual string Get( string name )
297                 {
298                         ArrayList values = (ArrayList)BaseGet(name);
299 /*                      if (values==null) 
300                                 Console.WriteLine("BaseGet returned null");*/
301                         return AsSingleString(values);
302 // -------------------------------------------------------------
303
304                 }
305                 /// <summary></summary>
306                 [MonoTODO]
307                 private static string AsSingleString(ArrayList values)
308                 {
309                         const char separator = ',';
310                         
311                         if (values==null)
312                                 return null;
313                         int max = values.Count;
314                         
315                         if (max==0)
316                                 return null;
317                         //TODO: reimplement this
318                         StringBuilder sb = new StringBuilder((string)values[0]);
319                         for (int i=1; i<max; i++){
320                                 sb.Append(separator);
321                                 sb.Append(values[i]);
322                         }
323
324                         return sb.ToString();                   
325                 }
326                 
327                 
328                 /// <summary>SDK: Gets the key at the specified index of the NameValueCollection.</summary>
329                 public virtual string GetKey( int index )
330                 {
331                         return BaseGetKey(index);
332                 }
333                 
334                 
335                 /// <summary>SDK: Gets the values at the specified index of the NameValueCollection.</summary>
336                  
337                 public virtual string[] GetValues( int index )
338                 {
339                         ArrayList values = (ArrayList)BaseGet(index);
340                         
341                         return AsStringArray(values);
342                 }
343                 
344                 
345                 public virtual string[] GetValues( string name )
346                 {
347                         ArrayList values = (ArrayList)BaseGet(name);
348                         
349                         return AsStringArray(values);
350                 }
351                 
352                 private static string[] AsStringArray(ArrayList values)
353                 {
354                         if (values == null)
355                                 return null;
356                         int max = values.Count;//get_Count();
357                         if (max==0)
358                                 return null;
359                         
360                         string[] valArray =new string[max];
361                         values.CopyTo(valArray);
362                         return valArray;
363                 }
364                 
365                 
366                 /// <summary>
367                 /// SDK: Gets a value indicating whether the NameValueCollection contains keys that
368                 /// are not a null reference 
369                 /// </summary>
370
371                 public bool HasKeys()
372                 {
373                         return BaseHasKeys();
374                 }
375                 
376                 public virtual void Remove( string name )
377                 {
378                         if (this.IsReadOnly)
379                                 throw new NotSupportedException("Collection is read-only");
380                         InvalidateCachedArrays();
381                         BaseRemove(name);
382                         
383                 }
384                 
385                 /// <summary>
386                 /// Sets the value of an entry in the NameValueCollection.
387                 /// </summary>
388                 public virtual void Set (string name, string value)
389                 {
390                         if (this.IsReadOnly)
391                                 throw new NotSupportedException ("Collection is read-only");
392
393                         InvalidateCachedArrays ();
394                         
395                         ArrayList values = new ArrayList ();
396                         if (value != null) {
397                                 values.Add (value);
398                                 BaseSet (name,values);
399                         }
400                         else {
401                                 // remove all entries
402                                 BaseSet (name, null);
403                         }
404                 }
405                 
406
407 //---------------------- Protected Instance Methods ----------------------      
408
409                 protected void InvalidateCachedArrays()
410                 {
411                         cachedAllKeys = null;
412                         cachedAll = null;
413                 }
414
415         }
416 }