2005-01-31 Zoltan Varga <vargaz@freemail.hu>
[mono.git] / mcs / class / Novell.Directory.Ldap / Novell.Directory.Ldap / LdapAttribute.cs
1 /******************************************************************************
2 * The MIT License
3 * Copyright (c) 2003 Novell Inc.  www.novell.com
4
5 * Permission is hereby granted, free of charge, to any person obtaining  a copy
6 * of this software and associated documentation files (the Software), to deal
7 * in the Software without restriction, including  without limitation the rights
8 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 
9 * copies of the Software, and to  permit persons to whom the Software is 
10 * furnished to do so, subject to the following conditions:
11
12 * The above copyright notice and this permission notice shall be included in 
13 * all copies or substantial portions of the Software.
14
15 * THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 * SOFTWARE.
22 *******************************************************************************/
23 //
24 // Novell.Directory.Ldap.LdapAttribute.cs
25 //
26 // Author:
27 //   Sunil Kumar (Sunilk@novell.com)
28 //
29 // (C) 2003 Novell, Inc (http://www.novell.com)
30 //
31
32 using System;
33 using ArrayEnumeration = Novell.Directory.Ldap.Utilclass.ArrayEnumeration;
34 using Base64 = Novell.Directory.Ldap.Utilclass.Base64;
35
36 namespace Novell.Directory.Ldap
37 {
38         /// <summary> The name and values of one attribute of a directory entry.
39         /// 
40         /// LdapAttribute objects are used when searching for, adding,
41         /// modifying, and deleting attributes from the directory.
42         /// LdapAttributes are often used in conjunction with an
43         /// {@link LdapAttributeSet} when retrieving or adding multiple
44         /// attributes to an entry.
45         /// 
46         /// 
47         /// 
48         /// </summary>
49         /// <seealso cref="LdapEntry">
50         /// </seealso>
51         /// <seealso cref="LdapAttributeSet">
52         /// </seealso>
53         /// <seealso cref="LdapModification">
54         /// </seealso>
55         
56         public class LdapAttribute : System.ICloneable, System.IComparable
57         {
58                 class URLData
59                 {
60                         private void  InitBlock(LdapAttribute enclosingInstance)
61                         {
62                                 this.enclosingInstance = enclosingInstance;
63                         }
64                         private LdapAttribute enclosingInstance;
65                         public LdapAttribute Enclosing_Instance
66                         {
67                                 get
68                                 {
69                                         return enclosingInstance;
70                                 }
71                                 
72                         }
73                         private int length;
74                         private sbyte[] data;
75                         public URLData(LdapAttribute enclosingInstance, sbyte[] data, int length)
76                         {
77                                 InitBlock(enclosingInstance);
78                                 this.length = length;
79                                 this.data = data;
80                                 return ;
81                         }
82                         public int getLength()
83                         {
84                                 return length;
85                         }
86                         public sbyte[] getData()
87                         {
88                                 return data;
89                         }
90                 }
91                 /// <summary> Returns an enumerator for the values of the attribute in byte format.
92                 /// 
93                 /// </summary>
94                 /// <returns> The values of the attribute in byte format.
95                 ///  Note: All string values will be UTF-8 encoded. To decode use the
96                 /// String constructor. Example: new String( byteArray, "UTF-8" );
97                 /// </returns>
98                 virtual public System.Collections.IEnumerator ByteValues
99                 {
100                         get
101                         {
102                                 return new ArrayEnumeration(ByteValueArray);
103                         }
104                         
105                 }
106                 /// <summary> Returns an enumerator for the string values of an attribute.
107                 /// 
108                 /// </summary>
109                 /// <returns> The string values of an attribute.
110                 /// </returns>
111                 virtual public System.Collections.IEnumerator StringValues
112                 {
113                         get
114                         {
115                                 return new ArrayEnumeration(StringValueArray);
116                         }
117                         
118                 }
119                 /// <summary> Returns the values of the attribute as an array of bytes.
120                 /// 
121                 /// </summary>
122                 /// <returns> The values as an array of bytes or an empty array if there are
123                 /// no values.
124                 /// </returns>
125                 [CLSCompliantAttribute(false)]
126                 virtual public sbyte[][] ByteValueArray
127                 {
128                         get
129                         {
130                                 if (null == this.values)
131                                         return new sbyte[0][];
132                                 int size = this.values.Length;
133                                 sbyte[][] bva = new sbyte[size][];
134                                 // Deep copy so application cannot change values
135                                 for (int i = 0, u = size; i < u; i++)
136                                 {
137                                         bva[i] = new sbyte[((sbyte[]) values[i]).Length];
138                                         Array.Copy((System.Array) this.values[i], 0, (System.Array) bva[i], 0, bva[i].Length);
139                                 }
140                                 return bva;
141                         }
142                         
143                 }
144                 /// <summary> Returns the values of the attribute as an array of strings.
145                 /// 
146                 /// </summary>
147                 /// <returns> The values as an array of strings or an empty array if there are
148                 /// no values
149                 /// </returns>
150                 virtual public System.String[] StringValueArray
151                 {
152                         get
153                         {
154                                 if (null == this.values)
155                                         return new System.String[0];
156                                 int size = values.Length;
157                                 System.String[] sva = new System.String[size];
158                                 for (int j = 0; j < size; j++)
159                                 {
160                                         try
161                                         {
162                                                 System.Text.Encoding encoder = System.Text.Encoding.GetEncoding("utf-8"); 
163                                                 char[] dchar = encoder.GetChars(SupportClass.ToByteArray((sbyte[])values[j]));
164 //                                              char[] dchar = encoder.GetChars((byte[])values[j]);
165                                                 sva[j] = new String(dchar);
166 //                                              sva[j] = new String((sbyte[]) values[j], "UTF-8");
167                                         }
168                                         catch (System.IO.IOException uee)
169                                         {
170                                                 // Exception should NEVER get thrown but just in case it does ...
171                                                 throw new System.SystemException(uee.ToString());
172                                         }
173                                 }
174                                 return sva;
175                         }
176                         
177                 }
178                 /// <summary> Returns the the first value of the attribute as a <code>String</code>.
179                 /// 
180                 /// </summary>
181                 /// <returns>  The UTF-8 encoded<code>String</code> value of the attribute's
182                 /// value.  If the value wasn't a UTF-8 encoded <code>String</code>
183                 /// to begin with the value of the returned <code>String</code> is
184                 /// non deterministic.
185                 /// 
186                 /// If <code>this</code> attribute has more than one value the
187                 /// first value is converted to a UTF-8 encoded <code>String</code>
188                 /// and returned. It should be noted, that the directory may
189                 /// return attribute values in any order, so that the first
190                 /// value may vary from one call to another.
191                 /// 
192                 /// If the attribute has no values <code>null</code> is returned
193                 /// </returns>
194                 virtual public System.String StringValue
195                 {
196                         get
197                         {
198                                 System.String rval = null;
199                                 if (this.values != null)
200                                 {
201                                         try
202                                         {
203                                                 System.Text.Encoding encoder = System.Text.Encoding.GetEncoding("utf-8"); 
204                                                 char[] dchar = encoder.GetChars(SupportClass.ToByteArray((sbyte[])this.values[0]));
205 //                                              char[] dchar = encoder.GetChars((byte[]) this.values[0]);
206                                                 rval = new String(dchar);
207                                         }
208                                         catch (System.IO.IOException use)
209                                         {
210                                                 throw new System.SystemException(use.ToString());
211                                         }
212                                 }
213                                 return rval;
214                         }
215                         
216                 }
217                 /// <summary> Returns the the first value of the attribute as a byte array.
218                 /// 
219                 /// </summary>
220                 /// <returns>  The binary value of <code>this</code> attribute or
221                 /// <code>null</code> if <code>this</code> attribute doesn't have a value.
222                 /// 
223                 /// If the attribute has no values <code>null</code> is returned
224                 /// </returns>
225                 [CLSCompliantAttribute(false)]
226                 virtual public sbyte[] ByteValue
227                 {
228                         get
229                         {
230                                 sbyte[] bva = null;
231                                 if (this.values != null)
232                                 {
233                                         // Deep copy so app can't change the value
234                                         bva = new sbyte[((sbyte[]) values[0]).Length];
235                                         Array.Copy((System.Array) this.values[0], 0, (System.Array) bva, 0, bva.Length);
236                                 }
237                                 return bva;
238                         }
239                         
240                 }
241                 /// <summary> Returns the language subtype of the attribute, if any.
242                 /// 
243                 /// For example, if the attribute name is cn;lang-ja;phonetic,
244                 /// this method returns the string, lang-ja.
245                 /// 
246                 /// </summary>
247                 /// <returns> The language subtype of the attribute or null if the attribute
248                 /// has none.
249                 /// </returns>
250                 virtual public System.String LangSubtype
251                 {
252                         get
253                         {
254                                 if (subTypes != null)
255                                 {
256                                         for (int i = 0; i < subTypes.Length; i++)
257                                         {
258                                                 if (subTypes[i].StartsWith("lang-"))
259                                                 {
260                                                         return subTypes[i];
261                                                 }
262                                         }
263                                 }
264                                 return null;
265                         }
266                         
267                 }
268                 /// <summary> Returns the name of the attribute.
269                 /// 
270                 /// </summary>
271                 /// <returns> The name of the attribute.
272                 /// </returns>
273                 virtual public System.String Name
274                 {
275                         get
276                         {
277                                 return name;
278                         }
279                         
280                 }
281                 /// <summary> Replaces all values with the specified value. This protected method is
282                 /// used by sub-classes of LdapSchemaElement because the value cannot be set
283                 /// with a contructor.
284                 /// </summary>
285                 virtual protected internal System.String Value
286                 {
287                         set
288                         {
289                                 values = null;
290                                 try
291                                 {
292                                         System.Text.Encoding encoder = System.Text.Encoding.GetEncoding("utf-8"); 
293                                         byte[] ibytes = encoder.GetBytes(value);
294                                         sbyte[] sbytes=SupportClass.ToSByteArray(ibytes);
295
296                                         this.add(sbytes);
297                                 }
298                                 catch (System.IO.IOException ue)
299                                 {
300                                         throw new System.SystemException(ue.ToString());
301                                 }
302                                 return ;
303                         }
304                         
305                 }
306                 private System.String name; // full attribute name
307                 private System.String baseName; // cn of cn;lang-ja;phonetic
308                 private System.String[] subTypes = null; // lang-ja of cn;lang-ja
309                 private System.Object[] values = null; // Array of byte[] attribute values
310                 
311                 /// <summary> Constructs an attribute with copies of all values of the input
312                 /// attribute.
313                 /// 
314                 /// </summary>
315                 /// <param name="attr"> An LdapAttribute to use as a template.
316                 /// 
317                 /// @throws IllegalArgumentException if attr is null
318                 /// </param>
319                 public LdapAttribute(LdapAttribute attr)
320                 {
321                         if (attr == null)
322                         {
323                                 throw new System.ArgumentException("LdapAttribute class cannot be null");
324                         }
325                         // Do a deep copy of the LdapAttribute template
326                         this.name = attr.name;
327                         this.baseName = attr.baseName;
328                         if (null != attr.subTypes)
329                         {
330                                 this.subTypes = new System.String[attr.subTypes.Length];
331                                 Array.Copy((System.Array) attr.subTypes, 0, (System.Array) this.subTypes, 0, this.subTypes.Length);
332                         }
333                         // OK to just copy attributes, as the app only sees a deep copy of them
334                         if (null != attr.values)
335                         {
336                                 this.values = new System.Object[attr.values.Length];
337                                 Array.Copy((System.Array) attr.values, 0, (System.Array) this.values, 0, this.values.Length);
338                         }
339                         return ;
340                 }
341                 
342                 /// <summary> Constructs an attribute with no values.
343                 /// 
344                 /// </summary>
345                 /// <param name="attrName">Name of the attribute.
346                 /// 
347                 /// @throws IllegalArgumentException if attrName is null
348                 /// </param>
349                 public LdapAttribute(System.String attrName)
350                 {
351                         if ((System.Object) attrName == null)
352                         {
353                                 throw new System.ArgumentException("Attribute name cannot be null");
354                         }
355                         this.name = attrName;
356                         this.baseName = LdapAttribute.getBaseName(attrName);
357                         this.subTypes = LdapAttribute.getSubtypes(attrName);
358                         return ;
359                 }
360                 
361                 /// <summary> Constructs an attribute with a byte-formatted value.
362                 /// 
363                 /// </summary>
364                 /// <param name="attrName">Name of the attribute.
365                 /// </param>
366                 /// <param name="attrBytes">Value of the attribute as raw bytes.
367                 /// 
368                 ///  Note: If attrBytes represents a string it should be UTF-8 encoded.
369                 /// 
370                 /// @throws IllegalArgumentException if attrName or attrBytes is null
371                 /// </param>
372                 [CLSCompliantAttribute(false)]
373                 public LdapAttribute(System.String attrName, sbyte[] attrBytes):this(attrName)
374                 {
375                         if (attrBytes == null)
376                         {
377                                 throw new System.ArgumentException("Attribute value cannot be null");
378                         }
379                         // Make our own copy of the byte array to prevent app from changing it
380                         sbyte[] tmp = new sbyte[attrBytes.Length];
381                         Array.Copy((System.Array) attrBytes, 0, (System.Array)tmp, 0, attrBytes.Length);
382                         this.add(tmp);
383                         return ;
384                 }
385                 
386                 /// <summary> Constructs an attribute with a single string value.
387                 /// 
388                 /// </summary>
389                 /// <param name="attrName">Name of the attribute.
390                 /// </param>
391                 /// <param name="attrString">Value of the attribute as a string.
392                 /// 
393                 /// @throws IllegalArgumentException if attrName or attrString is null
394                 /// </param>
395                 public LdapAttribute(System.String attrName, System.String attrString):this(attrName)
396                 {
397                         if ((System.Object) attrString == null)
398                         {
399                                 throw new System.ArgumentException("Attribute value cannot be null");
400                         }
401                         try
402                         {
403                                 System.Text.Encoding encoder = System.Text.Encoding.GetEncoding("utf-8"); 
404                                 byte[] ibytes = encoder.GetBytes(attrString);
405                                 sbyte[] sbytes=SupportClass.ToSByteArray(ibytes);
406
407                                 this.add(sbytes);
408                         }
409                         catch (System.IO.IOException e)
410                         {
411                                 throw new System.SystemException(e.ToString());
412                         }
413                         return ;
414                 }
415                 
416                 /// <summary> Constructs an attribute with an array of string values.
417                 /// 
418                 /// </summary>
419                 /// <param name="attrName">Name of the attribute.
420                 /// </param>
421                 /// <param name="attrStrings">Array of values as strings.
422                 /// 
423                 /// @throws IllegalArgumentException if attrName, attrStrings, or a member
424                 /// of attrStrings is null
425                 /// </param>
426                 public LdapAttribute(System.String attrName, System.String[] attrStrings):this(attrName)
427                 {
428                         if (attrStrings == null)
429                         {
430                                 throw new System.ArgumentException("Attribute values array cannot be null");
431                         }
432                         for (int i = 0, u = attrStrings.Length; i < u; i++)
433                         {
434                                 try
435                                 {
436                                         if ((System.Object) attrStrings[i] == null)
437                                         {
438                                                 throw new System.ArgumentException("Attribute value " + "at array index " + i + " cannot be null");
439                                         }
440                                         System.Text.Encoding encoder = System.Text.Encoding.GetEncoding("utf-8"); 
441                                         byte[] ibytes = encoder.GetBytes(attrStrings[i]);
442                                         sbyte[] sbytes=SupportClass.ToSByteArray(ibytes);
443                                         this.add(sbytes);
444 //                                      this.add(attrStrings[i].getBytes("UTF-8"));
445                                 }
446                                 catch (System.IO.IOException e)
447                                 {
448                                         throw new System.SystemException(e.ToString());
449                                 }
450                         }
451                         return ;
452                 }
453                 
454                 /// <summary> Returns a clone of this LdapAttribute.
455                 /// 
456                 /// </summary>
457                 /// <returns> clone of this LdapAttribute.
458                 /// </returns>
459                 public System.Object Clone()
460                 {
461                         try
462                         {
463                                 System.Object newObj = base.MemberwiseClone();
464                                 if (values != null)
465                                 {
466                                         Array.Copy((System.Array) this.values, 0, (System.Array) ((LdapAttribute) newObj).values, 0, this.values.Length);
467                                 }
468                                 return newObj;
469                         }
470                         catch (System.Exception ce)
471                         {
472                                 throw new System.SystemException("Internal error, cannot create clone");
473                         }
474                 }
475                 
476                 /// <summary> Adds a string value to the attribute.
477                 /// 
478                 /// </summary>
479                 /// <param name="attrString">Value of the attribute as a String.
480                 /// 
481                 /// @throws IllegalArgumentException if attrString is null
482                 /// </param>
483                 public virtual void  addValue(System.String attrString)
484                 {
485                         if ((System.Object) attrString == null)
486                         {
487                                 throw new System.ArgumentException("Attribute value cannot be null");
488                         }
489                         try
490                         {
491                                 System.Text.Encoding encoder = System.Text.Encoding.GetEncoding("utf-8"); 
492                                 byte[] ibytes = encoder.GetBytes(attrString);
493                                 sbyte[] sbytes=SupportClass.ToSByteArray(ibytes);
494                                 this.add(sbytes);
495 //                              this.add(attrString.getBytes("UTF-8"));
496                         }
497                         catch (System.IO.IOException ue)
498                         {
499                                 throw new System.SystemException(ue.ToString());
500                         }
501                         return ;
502                 }
503                 
504                 /// <summary> Adds a byte-formatted value to the attribute.
505                 /// 
506                 /// </summary>
507                 /// <param name="attrBytes">Value of the attribute as raw bytes.
508                 /// 
509                 ///  Note: If attrBytes represents a string it should be UTF-8 encoded.
510                 /// 
511                 /// @throws IllegalArgumentException if attrBytes is null
512                 /// </param>
513                 [CLSCompliantAttribute(false)]
514                 public virtual void  addValue(sbyte[] attrBytes)
515                 {
516                         if (attrBytes == null)
517                         {
518                                 throw new System.ArgumentException("Attribute value cannot be null");
519                         }
520                         this.add(attrBytes);
521                         return ;
522                 }
523                 
524                 /// <summary> Adds a base64 encoded value to the attribute.
525                 /// The value will be decoded and stored as bytes.  String
526                 /// data encoded as a base64 value must be UTF-8 characters.
527                 /// 
528                 /// </summary>
529                 /// <param name="attrString">The base64 value of the attribute as a String.
530                 /// 
531                 /// @throws IllegalArgumentException if attrString is null
532                 /// </param>
533                 public virtual void  addBase64Value(System.String attrString)
534                 {
535                         if ((System.Object) attrString == null)
536                         {
537                                 throw new System.ArgumentException("Attribute value cannot be null");
538                         }
539                         
540                         this.add(Base64.decode(attrString));
541                         return ;
542                 }
543                 
544                 /// <summary> Adds a base64 encoded value to the attribute.
545                 /// The value will be decoded and stored as bytes.  Character
546                 /// data encoded as a base64 value must be UTF-8 characters.
547                 /// 
548                 /// </summary>
549                 /// <param name="attrString">The base64 value of the attribute as a StringBuffer.
550                 /// </param>
551                 /// <param name="start"> The start index of base64 encoded part, inclusive.
552                 /// </param>
553                 /// <param name="end"> The end index of base encoded part, exclusive.
554                 /// 
555                 /// @throws IllegalArgumentException if attrString is null
556                 /// </param>
557                 public virtual void  addBase64Value(System.Text.StringBuilder attrString, int start, int end)
558                 {
559                         if (attrString == null)
560                         {
561                                 throw new System.ArgumentException("Attribute value cannot be null");
562                         }
563                         
564                         this.add(Base64.decode(attrString, start, end));
565                         
566                         return ;
567                 }
568                 
569                 /// <summary> Adds a base64 encoded value to the attribute.
570                 /// The value will be decoded and stored as bytes.  Character
571                 /// data encoded as a base64 value must be UTF-8 characters.
572                 /// 
573                 /// </summary>
574                 /// <param name="attrChars">The base64 value of the attribute as an array of
575                 /// characters.
576                 /// 
577                 /// @throws IllegalArgumentException if attrString is null
578                 /// </param>
579                 public virtual void  addBase64Value(char[] attrChars)
580                 {
581                         if (attrChars == null)
582                         {
583                                 throw new System.ArgumentException("Attribute value cannot be null");
584                         }
585                         
586                         this.add(Base64.decode(attrChars));
587                         return ;
588                 }
589                 
590                 /// <summary> Adds a URL, indicating a file or other resource that contains
591                 /// the value of the attribute.
592                 /// 
593                 /// </summary>
594                 /// <param name="url">String value of a URL pointing to the resource containing
595                 /// the value of the attribute.
596                 /// 
597                 /// @throws IllegalArgumentException if url is null
598                 /// </param>
599                 public virtual void  addURLValue(System.String url)
600                 {
601                         if ((System.Object) url == null)
602                         {
603                                 throw new System.ArgumentException("Attribute URL cannot be null");
604                         }
605                         addURLValue(new System.Uri(url));
606                         return ;
607                 }
608                 
609                 /// <summary> Adds a URL, indicating a file or other resource that contains
610                 /// the value of the attribute.
611                 /// 
612                 /// </summary>
613                 /// <param name="url">A URL class pointing to the resource containing the value
614                 /// of the attribute.
615                 /// 
616                 /// @throws IllegalArgumentException if url is null
617                 /// </param>
618                 public virtual void  addURLValue(System.Uri url)
619                 {
620                         // Class to encapsulate the data bytes and the length
621                         if (url == null)
622                         {
623                                 throw new System.ArgumentException("Attribute URL cannot be null");
624                         }
625                         try
626                         {
627                                 // Get InputStream from the URL
628                                 System.IO.Stream in_Renamed = System.Net.WebRequest.Create(url).GetResponse().GetResponseStream();
629                                 // Read the bytes into buffers and store the them in an arraylist
630                                 System.Collections.ArrayList bufs = new System.Collections.ArrayList();
631                                 sbyte[] buf = new sbyte[4096];
632                                 int len, totalLength = 0;
633                                 while ((len = SupportClass.ReadInput(in_Renamed, ref buf, 0, 4096)) != - 1)
634                                 {
635                                         bufs.Add(new URLData(this, buf, len));
636                                         buf = new sbyte[4096];
637                                         totalLength += len;
638                                 }
639                                 /*
640                                 * Now that the length is known, allocate an array to hold all
641                                 * the bytes of data and copy the data to that array, store
642                                 * it in this LdapAttribute
643                                 */
644                                 sbyte[] data = new sbyte[totalLength];
645                                 int offset = 0; //
646                                 for (int i = 0; i < bufs.Count; i++)
647                                 {
648                                         URLData b = (URLData) bufs[i];
649                                         len = b.getLength();
650                                         Array.Copy((System.Array) b.getData(), 0, (System.Array) data, offset, len);
651                                         offset += len;
652                                 }
653                                 this.add(data);
654                         }
655                         catch (System.IO.IOException ue)
656                         {
657                                 throw new System.SystemException(ue.ToString());
658                         }
659                         return ;
660                 }
661                 
662                 /// <summary> Returns the base name of the attribute.
663                 /// 
664                 /// For example, if the attribute name is cn;lang-ja;phonetic,
665                 /// this method returns cn.
666                 /// 
667                 /// </summary>
668                 /// <returns> The base name of the attribute.
669                 /// </returns>
670                 public virtual System.String getBaseName()
671                 {
672                         return baseName;
673                 }
674                 
675                 /// <summary> Returns the base name of the specified attribute name.
676                 /// 
677                 /// For example, if the attribute name is cn;lang-ja;phonetic,
678                 /// this method returns cn.
679                 /// 
680                 /// </summary>
681                 /// <param name="attrName">Name of the attribute from which to extract the
682                 /// base name.
683                 /// 
684                 /// </param>
685                 /// <returns> The base name of the attribute.
686                 /// 
687                 /// @throws IllegalArgumentException if attrName is null
688                 /// </returns>
689                 public static System.String getBaseName(System.String attrName)
690                 {
691                         if ((System.Object) attrName == null)
692                         {
693                                 throw new System.ArgumentException("Attribute name cannot be null");
694                         }
695                         int idx = attrName.IndexOf((System.Char) ';');
696                         if (- 1 == idx)
697                         {
698                                 return attrName;
699                         }
700                         return attrName.Substring(0, (idx) - (0));
701                 }
702                 
703                 /// <summary> Extracts the subtypes from the attribute name.
704                 /// 
705                 /// For example, if the attribute name is cn;lang-ja;phonetic,
706                 /// this method returns an array containing lang-ja and phonetic.
707                 /// 
708                 /// </summary>
709                 /// <returns> An array subtypes or null if the attribute has none.
710                 /// </returns>
711                 public virtual System.String[] getSubtypes()
712                 {
713                         return subTypes;
714                 }
715                 
716                 /// <summary> Extracts the subtypes from the specified attribute name.
717                 /// 
718                 /// For example, if the attribute name is cn;lang-ja;phonetic,
719                 /// this method returns an array containing lang-ja and phonetic.
720                 /// 
721                 /// </summary>
722                 /// <param name="attrName">  Name of the attribute from which to extract
723                 /// the subtypes.
724                 /// 
725                 /// </param>
726                 /// <returns> An array subtypes or null if the attribute has none.
727                 /// 
728                 /// @throws IllegalArgumentException if attrName is null
729                 /// </returns>
730                 public static System.String[] getSubtypes(System.String attrName)
731                 {
732                         if ((System.Object) attrName == null)
733                         {
734                                 throw new System.ArgumentException("Attribute name cannot be null");
735                         }
736                         SupportClass.Tokenizer st = new SupportClass.Tokenizer(attrName, ";");
737                         System.String[] subTypes = null;
738                         int cnt = st.Count;
739                         if (cnt > 0)
740                         {
741                                 st.NextToken(); // skip over basename
742                                 subTypes = new System.String[cnt - 1];
743                                 int i = 0;
744                                 while (st.HasMoreTokens())
745                                 {
746                                         subTypes[i++] = st.NextToken();
747                                 }
748                         }
749                         return subTypes;
750                 }
751                 
752                 /// <summary> Reports if the attribute name contains the specified subtype.
753                 /// 
754                 /// For example, if you check for the subtype lang-en and the
755                 /// attribute name is cn;lang-en, this method returns true.
756                 /// 
757                 /// </summary>
758                 /// <param name="subtype"> The single subtype to check for.
759                 /// 
760                 /// </param>
761                 /// <returns> True, if the attribute has the specified subtype;
762                 /// false, if it doesn't.
763                 /// 
764                 /// @throws IllegalArgumentException if subtype is null
765                 /// </returns>
766                 public virtual bool hasSubtype(System.String subtype)
767                 {
768                         if ((System.Object) subtype == null)
769                         {
770                                 throw new System.ArgumentException("subtype cannot be null");
771                         }
772                         if (null != this.subTypes)
773                         {
774                                 for (int i = 0; i < subTypes.Length; i++)
775                                 {
776                                         if (subTypes[i].ToUpper().Equals(subtype.ToUpper()))
777                                                 return true;
778                                 }
779                         }
780                         return false;
781                 }
782                 
783                 /// <summary> Reports if the attribute name contains all the specified subtypes.
784                 /// 
785                 ///  For example, if you check for the subtypes lang-en and phonetic
786                 /// and if the attribute name is cn;lang-en;phonetic, this method
787                 /// returns true. If the attribute name is cn;phonetic or cn;lang-en,
788                 /// this method returns false.
789                 /// 
790                 /// </summary>
791                 /// <param name="subtypes">  An array of subtypes to check for.
792                 /// 
793                 /// </param>
794                 /// <returns> True, if the attribute has all the specified subtypes;
795                 /// false, if it doesn't have all the subtypes.
796                 /// 
797                 /// @throws IllegalArgumentException if subtypes is null or if array member
798                 /// is null.
799                 /// </returns>
800                 public virtual bool hasSubtypes(System.String[] subtypes)
801                 {
802                         if (subtypes == null)
803                         {
804                                 throw new System.ArgumentException("subtypes cannot be null");
805                         }
806                         for (int i = 0; i < subtypes.Length; i++)
807                         {
808                                 for (int j = 0; j < subTypes.Length; j++)
809                                 {
810                                         if ((System.Object) subTypes[j] == null)
811                                         {
812                                                 throw new System.ArgumentException("subtype " + "at array index " + i + " cannot be null");
813                                         }
814                                         if (subTypes[j].ToUpper().Equals(subtypes[i].ToUpper()))
815                                         {
816                                                 goto gotSubType;
817                                         }
818                                 }
819                                 return false;
820 gotSubType: ;
821                         }
822                         return true;
823                 }
824                 
825                 /// <summary> Removes a string value from the attribute.
826                 /// 
827                 /// </summary>
828                 /// <param name="attrString">  Value of the attribute as a string.
829                 /// 
830                 /// Note: Removing a value which is not present in the attribute has
831                 /// no effect.
832                 /// 
833                 /// @throws IllegalArgumentException if attrString is null
834                 /// </param>
835                 public virtual void  removeValue(System.String attrString)
836                 {
837                         if (null == (System.Object) attrString)
838                         {
839                                 throw new System.ArgumentException("Attribute value cannot be null");
840                         }
841                         try
842                         {
843                                 System.Text.Encoding encoder = System.Text.Encoding.GetEncoding("utf-8"); 
844                                 byte[] ibytes = encoder.GetBytes(attrString);
845                                 sbyte[] sbytes=SupportClass.ToSByteArray(ibytes);
846                                 this.removeValue(sbytes);
847 //                              this.removeValue(attrString.getBytes("UTF-8"));
848                         }
849                         catch (System.IO.IOException uee)
850                         {
851                                 // This should NEVER happen but just in case ...
852                                 throw new System.SystemException(uee.ToString());
853                         }
854                         return ;
855                 }
856                 
857                 /// <summary> Removes a byte-formatted value from the attribute.
858                 /// 
859                 /// </summary>
860                 /// <param name="attrBytes">   Value of the attribute as raw bytes.
861                 ///  Note: If attrBytes represents a string it should be UTF-8 encoded.
862                 /// Example: <code>String.getBytes("UTF-8");</code>
863                 /// 
864                 /// Note: Removing a value which is not present in the attribute has
865                 /// no effect.
866                 /// 
867                 /// @throws IllegalArgumentException if attrBytes is null
868                 /// </param>
869                 [CLSCompliantAttribute(false)]
870                 public virtual void  removeValue(sbyte[] attrBytes)
871                 {
872                         if (null == attrBytes)
873                         {
874                                 throw new System.ArgumentException("Attribute value cannot be null");
875                         }
876                         for (int i = 0; i < this.values.Length; i++)
877                         {
878                                 if (equals(attrBytes, (sbyte[]) this.values[i]))
879                                 {
880                                         if (0 == i && 1 == this.values.Length)
881                                         {
882                                                 // Optimize if first element of a single valued attr
883                                                 this.values = null;
884                                                 return ;
885                                         }
886                                         if (this.values.Length == 1)
887                                         {
888                                                 this.values = null;
889                                         }
890                                         else
891                                         {
892                                                 int moved = this.values.Length - i - 1;
893                                                 System.Object[] tmp = new System.Object[this.values.Length - 1];
894                                                 if (i != 0)
895                                                 {
896                                                         Array.Copy((System.Array) values, 0, (System.Array) tmp, 0, i);
897                                                 }
898                                                 if (moved != 0)
899                                                 {
900                                                         Array.Copy((System.Array) values, i + 1, (System.Array) tmp, i, moved);
901                                                 }
902                                                 this.values = tmp;
903                                                 tmp = null;
904                                         }
905                                         break;
906                                 }
907                         }
908                         return ;
909                 }
910                 
911                 /// <summary> Returns the number of values in the attribute.
912                 /// 
913                 /// </summary>
914                 /// <returns> The number of values in the attribute.
915                 /// </returns>
916                 public virtual int size()
917                 {
918                         return null == this.values?0:this.values.Length;
919                 }
920                 
921                 /// <summary> Compares this object with the specified object for order.
922                 /// 
923                 ///  Ordering is determined by comparing attribute names (see
924                 /// {@link #getName() }) using the method compareTo() of the String class.
925                 /// 
926                 /// 
927                 /// </summary>
928                 /// <param name="attribute">  The LdapAttribute to be compared to this object.
929                 /// 
930                 /// </param>
931                 /// <returns>            Returns a negative integer, zero, or a positive
932                 /// integer as this object is less than, equal to, or greater than the
933                 /// specified object.
934                 /// </returns>
935                 public virtual int CompareTo(System.Object attribute)
936                 {
937                         
938                         return name.CompareTo(((LdapAttribute) attribute).name);
939                 }
940                 
941                 /// <summary> Adds an object to <code>this</code> object's list of attribute values
942                 /// 
943                 /// </summary>
944                 /// <param name="bytes">  Ultimately all of this attribute's values are treated
945                 /// as binary data so we simplify the process by requiring
946                 /// that all data added to our list is in binary form.
947                 /// 
948                 ///  Note: If attrBytes represents a string it should be UTF-8 encoded.
949                 /// </param>
950                 private void  add(sbyte[] bytes)
951                 {
952                         if (null == this.values)
953                         {
954                                 this.values = new System.Object[]{bytes};
955                         }
956                         else
957                         {
958                                 // Duplicate attribute values not allowed
959                                 for (int i = 0; i < this.values.Length; i++)
960                                 {
961                                         if (equals(bytes, (sbyte[]) this.values[i]))
962                                         {
963                                                 return ; // Duplicate, don't add
964                                         }
965                                 }
966                                 System.Object[] tmp = new System.Object[this.values.Length + 1];
967                                 Array.Copy((System.Array) this.values, 0, (System.Array) tmp, 0, this.values.Length);
968                                 tmp[this.values.Length] = bytes;
969                                 this.values = tmp;
970                                 tmp = null;
971                         }
972                         return ;
973                 }
974                 
975                 /// <summary> Returns true if the two specified arrays of bytes are equal to each
976                 /// another.  Matches the logic of Arrays.equals which is not available
977                 /// in jdk 1.1.x.
978                 /// 
979                 /// </summary>
980                 /// <param name="e1">the first array to be tested
981                 /// </param>
982                 /// <param name="e2">the second array to be tested
983                 /// </param>
984                 /// <returns> true if the two arrays are equal
985                 /// </returns>
986                 private bool equals(sbyte[] e1, sbyte[] e2)
987                 {
988                         // If same object, they compare true
989                         if (e1 == e2)
990                                 return true;
991                         
992                         // If either but not both are null, they compare false
993                         if (e1 == null || e2 == null)
994                                 return false;
995                         
996                         // If arrays have different length, they compare false
997                         int length = e1.Length;
998                         if (e2.Length != length)
999                                 return false;
1000                         
1001                         // If any of the bytes are different, they compare false
1002                         for (int i = 0; i < length; i++)
1003                         {
1004                                 if (e1[i] != e2[i])
1005                                         return false;
1006                         }
1007                         
1008                         return true;
1009                 }
1010                 
1011                 /// <summary> Returns a string representation of this LdapAttribute
1012                 /// 
1013                 /// </summary>
1014                 /// <returns> a string representation of this LdapAttribute
1015                 /// </returns>
1016                 public override System.String ToString()
1017                 {
1018                         System.Text.StringBuilder result = new System.Text.StringBuilder("LdapAttribute: ");
1019                         try
1020                         {
1021                                 result.Append("{type='" + name + "'");
1022                                 if (values != null)
1023                                 {
1024                                         result.Append(", ");
1025                                         if (values.Length == 1)
1026                                         {
1027                                                 result.Append("value='");
1028                                         }
1029                                         else
1030                                         {
1031                                                 result.Append("values='");
1032                                         }
1033                                         for (int i = 0; i < values.Length; i++)
1034                                         {
1035                                                 if (i != 0)
1036                                                 {
1037                                                         result.Append("','");
1038                                                 }
1039                                                 if (((sbyte[]) values[i]).Length == 0)
1040                                                 {
1041                                                         continue;
1042                                                 }
1043                                                 System.Text.Encoding encoder = System.Text.Encoding.GetEncoding("utf-8"); 
1044 //                                              char[] dchar = encoder.GetChars((byte[]) values[i]);
1045                                                 char[] dchar = encoder.GetChars(SupportClass.ToByteArray((sbyte[])values[i]));
1046                                                 System.String sval = new String(dchar);
1047
1048 //                                              System.String sval = new String((sbyte[]) values[i], "UTF-8");
1049                                                 if (sval.Length == 0)
1050                                                 {
1051                                                         // didn't decode well, must be binary
1052                                                         result.Append("<binary value, length:" + sval.Length);
1053                                                         continue;
1054                                                 }
1055                                                 result.Append(sval);
1056                                         }
1057                                         result.Append("'");
1058                                 }
1059                                 result.Append("}");
1060                         }
1061                         catch (System.Exception e)
1062                         {
1063                                 throw new System.SystemException(e.ToString());
1064                         }
1065                         return result.ToString();
1066                 }
1067         }
1068 }