1c59e3e4b1f21d385de7884dc7aec396988675e4
[mono.git] / mcs / class / System.XML / System.Xml / XmlConvert.cs
1 //
2 // System.Xml.XmlConvert
3 //
4 // Authors:
5 //      Dwivedi, Ajay kumar (Adwiv@Yahoo.com)
6 //      Gonzalo Paniagua Javier (gonzalo@ximian.com)
7 //      Alan Tam Siu Lung (Tam@SiuLung.com)
8 //      Atsushi Enomoto (ginga@kit.hi-ho.ne.jp)
9 //
10 // (C) 2002 Ximian, Inc (http://www.ximian.com)
11 //
12
13 //
14 // Permission is hereby granted, free of charge, to any person obtaining
15 // a copy of this software and associated documentation files (the
16 // "Software"), to deal in the Software without restriction, including
17 // without limitation the rights to use, copy, modify, merge, publish,
18 // distribute, sublicense, and/or sell copies of the Software, and to
19 // permit persons to whom the Software is furnished to do so, subject to
20 // the following conditions:
21 // 
22 // The above copyright notice and this permission notice shall be
23 // included in all copies or substantial portions of the Software.
24 // 
25 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
26 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
27 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
28 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
29 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
30 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
31 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
32 //
33 using System;
34 using System.IO;
35 using System.Text;
36 using System.Globalization;
37 using System.Xml.Schema;
38
39 namespace System.Xml {
40
41         public class XmlConvert {
42
43                 const string encodedColon = "_x003A_";
44                 const NumberStyles floatStyle = NumberStyles.AllowCurrencySymbol |
45                         NumberStyles.AllowExponent | 
46                         NumberStyles.AllowDecimalPoint |
47                         NumberStyles.AllowLeadingSign;
48                 
49                 static readonly string [] datetimeFormats = {
50                   // dateTime
51                   "yyyy-MM-ddTHH:mm:ss",
52                   "yyyy-MM-ddTHH:mm:ss.f",
53                   "yyyy-MM-ddTHH:mm:ss.ff",
54                   "yyyy-MM-ddTHH:mm:ss.fff",
55                   "yyyy-MM-ddTHH:mm:ss.ffff",
56                   "yyyy-MM-ddTHH:mm:ss.fffff",
57                   "yyyy-MM-ddTHH:mm:ss.ffffff",
58                   "yyyy-MM-ddTHH:mm:ss.fffffff",
59                   "yyyy-MM-ddTHH:mm:sszzz",
60                   "yyyy-MM-ddTHH:mm:ss.fzzz",
61                   "yyyy-MM-ddTHH:mm:ss.ffzzz",
62                   "yyyy-MM-ddTHH:mm:ss.fffzzz",
63                   "yyyy-MM-ddTHH:mm:ss.ffffzzz",
64                   "yyyy-MM-ddTHH:mm:ss.fffffzzz",
65                   "yyyy-MM-ddTHH:mm:ss.ffffffzzz",
66                   "yyyy-MM-ddTHH:mm:ss.fffffffzzz",
67                   "yyyy-MM-ddTHH:mm:ssZ",
68                   "yyyy-MM-ddTHH:mm:ss.fZ",
69                   "yyyy-MM-ddTHH:mm:ss.ffZ",
70                   "yyyy-MM-ddTHH:mm:ss.fffZ",
71                   "yyyy-MM-ddTHH:mm:ss.ffffZ",
72                   "yyyy-MM-ddTHH:mm:ss.fffffZ",
73                   "yyyy-MM-ddTHH:mm:ss.ffffffZ",
74                   "yyyy-MM-ddTHH:mm:ss.fffffffZ",
75                   // time
76                   "HH:mm:ss",
77                   "HH:mm:ss.f",
78                   "HH:mm:ss.ff",
79                   "HH:mm:ss.fff",
80                   "HH:mm:ss.ffff",
81                   "HH:mm:ss.fffff",
82                   "HH:mm:ss.ffffff",
83                   "HH:mm:ss.fffffff",
84                   "HH:mm:sszzz",
85                   "HH:mm:ss.fzzz",
86                   "HH:mm:ss.ffzzz",
87                   "HH:mm:ss.fffzzz",
88                   "HH:mm:ss.ffffzzz",
89                   "HH:mm:ss.fffffzzz",
90                   "HH:mm:ss.ffffffzzz",
91                   "HH:mm:ss.fffffffzzz",
92                   "HH:mm:ssZ",
93                   "HH:mm:ss.fZ",
94                   "HH:mm:ss.ffZ",
95                   "HH:mm:ss.fffZ",
96                   "HH:mm:ss.ffffZ",
97                   "HH:mm:ss.fffffZ",
98                   "HH:mm:ss.ffffffZ",
99                   "HH:mm:ss.fffffffZ",
100                   // date
101                   "yyyy-MM-dd",
102                   "yyyy-MM-ddzzz",
103                   "yyyy-MM-ddZ",
104                   // gYearMonth
105                   "yyyy-MM",
106                   "yyyy-MMzzz",
107                   "yyyy-MMZ",
108                   // gYear
109                   "yyyy",
110                   "yyyyzzz",
111                   "yyyyZ",
112                   // gMonthDay
113                   "--MM-dd",
114                   "--MM-ddzzz",
115                   "--MM-ddZ",
116                   // gDay
117                   "---dd",
118                   "---ddzzz",
119                   "---ddZ",
120                 };
121                 
122                 public XmlConvert()
123                 {}
124
125                 private static string TryDecoding (string s)
126                 {
127                         if (s == null || s.Length < 6)
128                                 return s;
129
130                         char c = '\uFFFF';
131                         try {
132                                 c = (char) Int32.Parse (s.Substring (1, 4), NumberStyles.HexNumber, CultureInfo.InvariantCulture);
133                         } catch {
134                                 return s [0] + DecodeName (s.Substring (1));
135                         }
136                         
137                         if (s.Length == 6)
138                                 return c.ToString ();
139                         return c + DecodeName (s.Substring (6));
140                 }
141                 
142                 public static string DecodeName (string name)
143                 {
144                         if (name == null || name.Length == 0)
145                                 return name;
146
147                         int pos = name.IndexOf ('_');
148                         if (pos == -1 || pos + 6 >= name.Length)
149                                 return name;
150
151                         if ((name [pos + 1] != 'X' && name [pos + 1] != 'x') || name [pos + 6] != '_')
152                                 return name [0] + DecodeName (name.Substring (1));
153
154                         return name.Substring (0, pos) + TryDecoding (name.Substring (pos + 1));
155                 }
156
157                 public static string EncodeLocalName (string name)
158                 {
159                         if (name == null)
160                                 return name;
161
162                         string encoded = EncodeName (name);
163                         int pos = encoded.IndexOf (':');
164                         if (pos == -1)
165                                 return encoded;
166                         return encoded.Replace (":", encodedColon);
167                 }
168
169                 internal static bool IsInvalid (char c, bool firstOnlyLetter)
170                 {
171                         if (c == ':') // Special case. allowed in EncodeName, but encoded in EncodeLocalName
172                                 return false;
173                         
174                         if (firstOnlyLetter)
175                                 return !XmlChar.IsFirstNameChar (c);
176                         else
177                                 return !XmlChar.IsNameChar (c);
178                 }
179
180                 private static string EncodeName (string name, bool nmtoken)
181                 {
182                         if (name == null || name.Length == 0)
183                                 return name;
184
185                         StringBuilder sb = new StringBuilder ();
186                         int length = name.Length;
187                         for (int i = 0; i < length; i++) {
188                                 char c = name [i];
189                                 if (IsInvalid (c, i == 0 && !nmtoken))
190                                         sb.AppendFormat ("_x{0:X4}_", (int) c);
191                                 else if (c == '_' && i + 6 < length && name [i+1] == 'x' && name [i + 6] == '_')
192                                         sb.Append ("_x005F_");
193                                 else
194                                         sb.Append (c);
195                         }
196                         return sb.ToString ();
197                 }
198
199                 public static string EncodeName (string name)
200                 {
201                         return EncodeName (name, false);
202                 }
203                 
204                 public static string EncodeNmToken (string name)
205                 {
206                         if (name == String.Empty)
207                                 throw new XmlException ("Invalid NmToken: ''");
208                         return EncodeName (name, true);
209                 }
210
211                 // {true, false, 1, 0}
212                 public static bool ToBoolean(string s)
213                 {
214                         s = s.Trim (XmlChar.WhitespaceChars);
215                         switch(s)
216                         {
217                                 case "1":
218                                         return true;
219                                 case "true":
220                                         return true;
221                                 case "0":
222                                         return false;
223                                 case "false":
224                                         return false;
225                                 default:
226                                         throw new FormatException(s + " is not a valid boolean value");
227                         }
228                 }
229
230                 // LAMESPEC: It has been documented as public, but is marked as internal.
231                 internal static string ToBinHexString (byte [] buffer)
232                 {
233                         StringWriter w = new StringWriter ();
234                         WriteBinHex (buffer, 0, buffer.Length, w);
235                         return w.ToString ();
236                 }
237
238                 internal static void WriteBinHex (byte [] buffer, int index, int count, TextWriter w)
239                 {
240                         if (buffer == null)
241                                 throw new ArgumentNullException ("buffer");
242                         if (index < 0)
243                                 throw new ArgumentOutOfRangeException ("index", index, "index must be non negative integer.");
244                         if (count < 0)
245                                 throw new ArgumentOutOfRangeException ("count", count, "count must be non negative integer.");
246                         if (buffer.Length < index + count)
247                                 throw new ArgumentOutOfRangeException ("index and count must be smaller than the length of the buffer.");
248
249                         // Copied from XmlTextWriter.WriteBinHex ()
250                         int end = index + count;
251                         for (int i = index; i < end; i++) {
252                                 int val = buffer [i];
253                                 int high = val >> 4;
254                                 int low = val & 15;
255                                 if (high > 9)
256                                         w.Write ((char) (high + 55));
257                                 else
258                                         w.Write ((char) (high + 0x30));
259                                 if (low > 9)
260                                         w.Write ((char) (low + 55));
261                                 else
262                                         w.Write ((char) (low + 0x30));
263                         }
264                 }
265
266                 public static byte ToByte(string s)
267                 {
268                         return Byte.Parse(s, CultureInfo.InvariantCulture);
269                 }
270
271                 public static char ToChar(string s)
272                 {
273                         return Char.Parse(s);
274                 }
275
276 #if NET_2_0
277                 [Obsolete]
278 #endif
279                 public static DateTime ToDateTime (string s)
280                 {
281                         return ToDateTime (s, datetimeFormats);
282                 }
283                 
284 #if NET_2_0
285                 public static DateTime ToDateTime (string value, XmlDateTimeSerializationMode mode)
286                 {
287                         string modestr = null;
288                         switch (mode) {
289                         case XmlDateTimeSerializationMode.Local:
290                         case XmlDateTimeSerializationMode.RoundTripKind:
291                         default:
292                                 return ToDateTime (value, "yyyy-MM-ddTHH:mm:ss.fffffffzzz");
293                         case XmlDateTimeSerializationMode.Utc:
294                                 return ToDateTime (value, "yyyy-MM-ddTHH:mm:ss.fffffffZ").ToUniversalTime ();
295                         case XmlDateTimeSerializationMode.Unspecified:
296                                 return ToDateTime (value, "yyyy-MM-ddTHH:mm:ss.fffffff");
297                         }
298                 }
299 #endif
300                 public static DateTime ToDateTime(string s, string format)
301                 {
302                         DateTimeFormatInfo d = new DateTimeFormatInfo();
303                         d.FullDateTimePattern = format;
304                         return DateTime.Parse(s, d);
305                 }
306
307                 public static DateTime ToDateTime(string s, string[] formats)
308                 {
309                         DateTimeStyles style = DateTimeStyles.AllowLeadingWhite |
310                                                DateTimeStyles.AllowTrailingWhite;
311                         return DateTime.ParseExact (s, formats, DateTimeFormatInfo.InvariantInfo, style);
312                 }
313                 
314                 public static Decimal ToDecimal(string s)
315                 {
316                         return Decimal.Parse(s, CultureInfo.InvariantCulture);
317                 }
318
319                 public static double ToDouble(string s)
320                 {
321                         if (s == null)
322                                 throw new ArgumentNullException();
323                         if (s == "INF")
324                                 return Double.PositiveInfinity;
325                         if (s == "-INF")
326                                 return Double.NegativeInfinity;
327                         if (s == "NaN")
328                                 return Double.NaN;
329                         return Double.Parse (s, floatStyle, CultureInfo.InvariantCulture);
330                 }
331
332                 public static Guid ToGuid(string s)
333                 {
334                         return new Guid(s);
335                 }
336
337                 public static short ToInt16(string s)
338                 {
339                         return Int16.Parse(s, CultureInfo.InvariantCulture);
340                 }
341
342                 public static int ToInt32(string s)
343                 {
344                         return Int32.Parse(s, CultureInfo.InvariantCulture);
345                 }
346
347                 public static long ToInt64(string s)
348                 {
349                         return Int64.Parse(s, CultureInfo.InvariantCulture);
350                 }
351
352                 [CLSCompliant (false)]
353                 public static SByte ToSByte(string s)
354                 {
355                         return SByte.Parse(s, CultureInfo.InvariantCulture);
356                 }
357
358                 public static float ToSingle(string s)
359                 {
360                         if (s == null)
361                                 throw new ArgumentNullException();
362                         if (s == "INF")
363                                 return Single.PositiveInfinity;
364                         if (s == "-INF")
365                                 return Single.NegativeInfinity;
366                         if (s == "NaN")
367                                 return Single.NaN;
368                         return Single.Parse(s, floatStyle, CultureInfo.InvariantCulture);
369                 }
370
371                 public static string ToString(Guid value)
372                 {
373                         return value.ToString("D", CultureInfo.InvariantCulture);
374                 }
375
376                 public static string ToString(int value)
377                 {
378                         return value.ToString(CultureInfo.InvariantCulture);
379                 }
380
381                 public static string ToString(short value)
382                 {
383                         return value.ToString(CultureInfo.InvariantCulture);
384                 }
385
386                 public static string ToString(byte value)
387                 {
388                         return value.ToString(CultureInfo.InvariantCulture);
389                 }
390
391                 public static string ToString(long value)
392                 {
393                         return value.ToString(CultureInfo.InvariantCulture);
394                 }
395
396                 public static string ToString(char value)
397                 {
398                         return value.ToString(CultureInfo.InvariantCulture);
399                 }
400
401                 public static string ToString(bool value)
402                 {
403                         if (value) return "true";
404                         return "false";
405                 }
406
407                 [CLSCompliant (false)]
408                 public static string ToString(SByte value)
409                 {
410                         return value.ToString(CultureInfo.InvariantCulture);
411                 }
412
413                 public static string ToString(Decimal value)
414                 {
415                         return value.ToString (CultureInfo.InvariantCulture);
416                 }
417
418                 [CLSCompliant (false)]
419                 public static string ToString(UInt64 value)
420                 {
421                         return value.ToString(CultureInfo.InvariantCulture);
422                 }
423
424                 public static string ToString (TimeSpan value)
425                 {
426                         StringBuilder builder = new StringBuilder ();
427                         if (value.Ticks < 0) {
428                                 builder.Append ('-');
429                                 value = value.Negate ();
430                         }
431                         builder.Append ('P');
432                         if (value.Days > 0)
433                                 builder.Append (value.Days).Append ('D');
434                         if (value.Days > 0 || value.Hours > 0 || value.Minutes > 0 || value.Seconds > 0 || value.Milliseconds > 0) {
435                                 builder.Append('T');
436                                 if (value.Hours > 0)
437                                         builder.Append (value.Hours).Append ('H');
438                                 if (value.Minutes > 0) 
439                                         builder.Append (value.Minutes).Append ('M');
440                                 if (value.Seconds > 0 || value.Milliseconds > 0) {
441                                         builder.Append (value.Seconds);
442                                         if (value.Milliseconds > 0)
443                                                 builder.Append ('.').AppendFormat ("{0:000}", value.Milliseconds);
444                                         builder.Append ('S');
445                                 }
446                         }
447                         return builder.ToString ();
448                 }
449
450                 public static string ToString(double value)
451                 {
452                         if (Double.IsNegativeInfinity(value)) return "-INF";
453                         if (Double.IsPositiveInfinity(value)) return "INF";
454                         if (Double.IsNaN(value)) return "NaN";
455 #if TARGET_JVM
456                         return value.ToString("R", NumberFormatInfo.InvariantInfo);
457 #else
458                         return value.ToString(CultureInfo.InvariantCulture);
459 #endif
460                 }
461
462                 public static string ToString(float value)
463                 {
464                         if (Single.IsNegativeInfinity(value)) return "-INF";
465                         if (Single.IsPositiveInfinity(value)) return "INF";
466                         if (Single.IsNaN(value)) return "NaN";
467 #if TARGET_JVM
468                         return value.ToString("R", NumberFormatInfo.InvariantInfo);
469 #else
470                         return value.ToString(CultureInfo.InvariantCulture);
471 #endif
472                 }
473
474                 [CLSCompliant (false)]
475                 public static string ToString(UInt32 value)
476                 {
477                         return value.ToString(CultureInfo.InvariantCulture);
478                 }
479
480                 [CLSCompliant (false)]
481                 public static string ToString(UInt16 value)
482                 {
483                         return value.ToString(CultureInfo.InvariantCulture);
484                 }
485
486 #if NET_2_0
487                 [Obsolete]
488 #endif
489                 public static string ToString (DateTime value)
490                 {
491                         return value.ToString ("yyyy-MM-ddTHH:mm:ss.fffffffzzz", CultureInfo.InvariantCulture);
492                 }
493
494 #if NET_2_0
495                 public static string ToString (DateTime value, XmlDateTimeSerializationMode mode)
496                 {
497                         string modestr = null;
498                         switch (mode) {
499                         case XmlDateTimeSerializationMode.Local:
500                         case XmlDateTimeSerializationMode.RoundTripKind:
501                         default:
502                                 return value.ToString (
503                                         "yyyy-MM-ddTHH:mm:ss.fffffffzzz",
504                                         CultureInfo.InvariantCulture);
505                                 break;
506                         case XmlDateTimeSerializationMode.Utc:
507                                 return value.ToUniversalTime ().ToString (
508                                         "yyyy-MM-ddTHH:mm:ss.fffffffZ",
509                                         CultureInfo.InvariantCulture);
510                                 break;
511                         case XmlDateTimeSerializationMode.Unspecified:
512                                 return value.ToString (
513                                         "yyyy-MM-ddTHH:mm:ss.fffffff",
514                                         CultureInfo.InvariantCulture);
515                                 break;
516                         }
517                 }
518 #endif
519
520                 public static string ToString(DateTime value, string format)
521                 {
522                         return value.ToString(format, CultureInfo.InvariantCulture);
523                 }
524
525                 public static TimeSpan ToTimeSpan(string s)
526                 {
527                         if (s.Length == 0)
528                                 throw new ArgumentException ("Invalid format string for duration schema datatype.");
529
530                         int start = 0;
531                         if (s [0] == '-')
532                                 start = 1;
533                         bool minusValue = (start == 1);
534
535                         if (s [start] != 'P')
536                                 throw new ArgumentException ("Invalid format string for duration schema datatype.");
537                         start++;
538
539                         int parseStep = 0;
540                         int days = 0;
541                         bool isTime = false;
542                         int hours = 0;
543                         int minutes = 0;
544                         int seconds = 0;
545
546                         bool error = false;
547
548                         int i = start;
549                         while (i < s.Length) {
550                                 if (s [i] == 'T') {
551                                         isTime = true;
552                                         parseStep = 4;
553                                         i++;
554                                         start = i;
555                                         continue;
556                                 }
557                                 for (; i < s.Length; i++) {
558                                         if (!Char.IsDigit (s [i]))
559                                                 break;
560                                 }
561                                 int value = int.Parse (s.Substring (start, i - start), CultureInfo.InvariantCulture);
562                                 switch (s [i]) {
563                                 case 'Y':
564                                         days += value * 365;
565                                         if (parseStep > 0)
566                                                 error = true;
567                                         else
568                                                 parseStep = 1;
569                                         break;
570                                 case 'M':
571                                         if (parseStep < 2) {
572                                                 days += 365 * (value / 12) + 30 * (value % 12);
573                                                 parseStep = 2;
574                                         } else if (isTime && parseStep < 6) {
575                                                 minutes = value;
576                                                 parseStep = 6;
577                                         }
578                                         else
579                                                 error = true;
580                                         break;
581                                 case 'D':
582                                         days += value;
583                                         if (parseStep > 2)
584                                                 error = true;
585                                         else
586                                                 parseStep = 3;
587                                         break;
588                                 case 'H':
589                                         hours = value;
590                                         if (!isTime || parseStep > 4)
591                                                 error = true;
592                                         else
593                                                 parseStep = 5;
594                                         break;
595                                 case 'S':
596                                         seconds = value;
597                                         if (!isTime || parseStep > 6)
598                                                 error = true;
599                                         else
600                                                 parseStep = 7;
601                                         break;
602                                 default:
603                                         error = true;
604                                         break;
605                                 }
606                                 if (error)
607                                         break;
608                                 ++i;
609                                 start = i;
610                         }
611                         if (error)
612                                 throw new ArgumentException ("Invalid format string for duration schema datatype.");
613                         TimeSpan ts = new TimeSpan (days, hours, minutes, seconds);
614                         return minusValue ? -ts : ts;
615                 }
616
617                 [CLSCompliant (false)]
618                 public static UInt16 ToUInt16(string s)
619                 {
620                         return UInt16.Parse(s, CultureInfo.InvariantCulture);
621                 }
622
623                 [CLSCompliant (false)]
624                 public static UInt32 ToUInt32(string s)
625                 {
626                         return UInt32.Parse(s, CultureInfo.InvariantCulture);
627                 }
628
629                 [CLSCompliant (false)]
630                 public static UInt64 ToUInt64(string s)
631                 {
632                         return UInt64.Parse(s, CultureInfo.InvariantCulture);
633                 }
634
635                 public static string VerifyName (string name)
636                 {
637                         if (name == null)
638                                 throw new ArgumentNullException("name");
639
640                         if (!XmlChar.IsName (name))
641                                 throw new XmlException("'" + name + "' is not a valid XML Name");
642                         return name;
643                         
644                 }
645
646                 public static string VerifyNCName (string ncname)
647                 {
648                         if (ncname == null)
649                                 throw new ArgumentNullException("ncname");
650
651                         if (!XmlChar.IsNCName (ncname))
652                                 throw new XmlException ("'" + ncname + "' is not a valid XML NCName");
653                         return ncname;
654                 }
655
656 #if NET_2_0
657                 public static string VerifyNMTOKEN (string name)
658 #else
659                 internal static string VerifyNMTOKEN (string name)
660 #endif
661                 {
662                         if (name == null)
663                                 throw new ArgumentNullException("name");
664
665                         if (!XmlChar.IsNmToken (name))
666                                 throw new XmlException("'" + name + "' is not a valid XML NMTOKEN");
667                         return name;
668                         
669                 }
670
671                 // It is documented as public method, but in fact it is not.
672                 internal static byte [] FromBinHexString (string s)
673                 {
674                         char [] chars = s.ToCharArray ();
675                         byte [] bytes = new byte [chars.Length / 2 + chars.Length % 2];
676                         FromBinHexString (chars, 0, chars.Length, bytes);
677                         return bytes;
678                 }
679
680                 internal static int FromBinHexString (char [] chars, int offset, int charLength, byte [] buffer)
681                 {
682                         int bufIndex = offset;
683                         for (int i = 0; i < charLength - 1; i += 2) {
684                                 buffer [bufIndex] = (chars [i] > '9' ?
685                                                 (byte) (chars [i] - 'A' + 10) :
686                                                 (byte) (chars [i] - '0'));
687                                 buffer [bufIndex] <<= 4;
688                                 buffer [bufIndex] += chars [i + 1] > '9' ?
689                                                 (byte) (chars [i + 1] - 'A' + 10) : 
690                                                 (byte) (chars [i + 1] - '0');
691                                 bufIndex++;
692                         }
693                         if (charLength %2 != 0)
694                                 buffer [bufIndex++] = (byte)
695                                         ((chars [charLength - 1] > '9' ?
696                                                 (byte) (chars [charLength - 1] - 'A' + 10) :
697                                                 (byte) (chars [charLength - 1] - '0'))
698                                         << 4);
699
700                         return bufIndex - offset;
701                 }
702         }
703 }