KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > javax > xml > bind > DatatypeConverterImpl


1 package javax.xml.bind;
2
3 import javax.xml.namespace.QName JavaDoc;
4 import javax.xml.namespace.NamespaceContext JavaDoc;
5 import javax.xml.datatype.DatatypeFactory JavaDoc;
6 import javax.xml.datatype.DatatypeConfigurationException JavaDoc;
7 import java.math.BigInteger JavaDoc;
8 import java.math.BigDecimal JavaDoc;
9 import java.util.Calendar JavaDoc;
10 import java.util.GregorianCalendar JavaDoc;
11 import java.util.TimeZone JavaDoc;
12
13 /**
14  * This class is the JAXB RI's default implementation of the
15  * {@link DatatypeConverterInterface}.
16  *
17  * <p>
18  * When client apps specify the use of the static print/parse
19  * methods in {@link DatatypeConverter}, it will delegate
20  * to this class.
21  *
22  * <p>
23  * This class is responsible for whitespace normalization.
24  *
25  * @author <ul><li>Ryan Shoemaker, Sun Microsystems, Inc.</li></ul>
26  * @version $Revision$
27  * @since JAXB2.1
28  */

29 final class DatatypeConverterImpl implements DatatypeConverterInterface {
30
31     /**
32      * To avoid re-creating instances, we cache one instance.
33      */

34     public static final DatatypeConverterInterface theInstance = new DatatypeConverterImpl();
35
36     protected DatatypeConverterImpl() {
37     }
38
39     public String JavaDoc parseString(String JavaDoc lexicalXSDString) {
40         return lexicalXSDString;
41     }
42
43     public BigInteger JavaDoc parseInteger(String JavaDoc lexicalXSDInteger) {
44         return _parseInteger(lexicalXSDInteger);
45     }
46
47     public static BigInteger JavaDoc _parseInteger(CharSequence JavaDoc s) {
48         return new BigInteger JavaDoc(removeOptionalPlus(WhiteSpaceProcessor.trim(s)).toString());
49     }
50
51     public String JavaDoc printInteger(BigInteger JavaDoc val) {
52         return _printInteger(val);
53     }
54
55     public static String JavaDoc _printInteger(BigInteger JavaDoc val) {
56         return val.toString();
57     }
58
59     public int parseInt(String JavaDoc s) {
60         return _parseInt(s);
61     }
62
63     /**
64      * Faster but less robust String->int conversion.
65      *
66      * Note that:
67      * <ol>
68      * <li>XML Schema allows '+', but {@link Integer#valueOf(String)} is not.
69      * <li>XML Schema allows leading and trailing (but not in-between) whitespaces..
70      * {@link Integer#valueOf(String)} doesn't allow any.
71      * </ol>
72      */

73     public static int _parseInt(CharSequence JavaDoc s) {
74         int len = s.length();
75         int sign = 1;
76
77         int r = 0;
78
79         for( int i=0; i<len; i++ ) {
80             char ch = s.charAt(i);
81             if(WhiteSpaceProcessor.isWhiteSpace(ch)) {
82                 // skip whitespace
83
} else
84             if('0'<=ch && ch<='9') {
85                 r = r*10 + (ch-'0');
86             } else
87             if(ch=='-') {
88                 sign = -1;
89             } else
90             if(ch=='+') {
91                 // noop
92
} else
93                 throw new NumberFormatException JavaDoc("Not a number: "+s);
94         }
95
96         return r*sign;
97     }
98
99     public long parseLong(String JavaDoc lexicalXSLong) {
100         return _parseLong(lexicalXSLong);
101     }
102
103     public static long _parseLong(CharSequence JavaDoc s) {
104         return Long.valueOf(removeOptionalPlus(WhiteSpaceProcessor.trim(s)).toString());
105     }
106
107     public short parseShort(String JavaDoc lexicalXSDShort) {
108         return _parseShort(lexicalXSDShort);
109     }
110
111     public static short _parseShort(CharSequence JavaDoc s) {
112         return (short)_parseInt(s);
113     }
114
115     public String JavaDoc printShort(short val) {
116         return _printShort(val);
117     }
118
119     public static String JavaDoc _printShort(short val) {
120         return String.valueOf(val);
121     }
122
123     public BigDecimal JavaDoc parseDecimal(String JavaDoc content) {
124         return _parseDecimal(content);
125     }
126     public static BigDecimal JavaDoc _parseDecimal(CharSequence JavaDoc content) {
127         content = WhiteSpaceProcessor.trim(content);
128
129         return new BigDecimal JavaDoc(content.toString());
130
131         // from purely XML Schema perspective,
132
// this implementation has a problem, since
133
// in xs:decimal "1.0" and "1" is equal whereas the above
134
// code will return different values for those two forms.
135
//
136
// the code was originally using com.sun.msv.datatype.xsd.NumberType.load,
137
// but a profiling showed that the process of normalizing "1.0" into "1"
138
// could take non-trivial time.
139
//
140
// also, from the user's point of view, one might be surprised if
141
// 1 (not 1.0) is returned from "1.000"
142
}
143
144     public float parseFloat(String JavaDoc lexicalXSDFloat) {
145         return _parseFloat(lexicalXSDFloat);
146     }
147
148     public static float _parseFloat( CharSequence JavaDoc _val ) {
149         String JavaDoc s = WhiteSpaceProcessor.trim(_val).toString();
150         /* Incompatibilities of XML Schema's float "xfloat" and Java's float "jfloat"
151
152             * jfloat.valueOf ignores leading and trailing whitespaces,
153               whereas this is not allowed in xfloat.
154             * jfloat.valueOf allows "float type suffix" (f, F) to be
155               appended after float literal (e.g., 1.52e-2f), whereare
156               this is not the case of xfloat.
157
158             gray zone
159             ---------
160             * jfloat allows ".523". And there is no clear statement that mentions
161               this case in xfloat. Although probably this is allowed.
162             *
163         */

164
165         if(s.equals("NaN")) return Float.NaN;
166         if(s.equals("INF")) return Float.POSITIVE_INFINITY;
167         if(s.equals("-INF")) return Float.NEGATIVE_INFINITY;
168
169         if(s.length()==0
170         || !isDigitOrPeriodOrSign(s.charAt(0))
171         || !isDigitOrPeriodOrSign(s.charAt(s.length()-1)) )
172             throw new NumberFormatException JavaDoc();
173
174         // these screening process is necessary due to the wobble of Float.valueOf method
175
return Float.parseFloat(s);
176     }
177
178     public String JavaDoc printFloat(float v) {
179         return _printFloat(v);
180     }
181
182     public static String JavaDoc _printFloat(float v) {
183         if( v==Float.NaN ) return "NaN";
184         if( v==Float.POSITIVE_INFINITY ) return "INF";
185         if( v==Float.NEGATIVE_INFINITY ) return "-INF";
186         return String.valueOf(v);
187     }
188
189
190
191     public double parseDouble(String JavaDoc lexicalXSDDouble) {
192         return _parseDouble(lexicalXSDDouble);
193     }
194
195     public static double _parseDouble( CharSequence JavaDoc _val ) {
196         String JavaDoc val = WhiteSpaceProcessor.trim(_val).toString();
197
198         if(val.equals("NaN")) return Double.NaN;
199         if(val.equals("INF")) return Double.POSITIVE_INFINITY;
200         if(val.equals("-INF")) return Double.NEGATIVE_INFINITY;
201
202         if(val.length()==0
203         || !isDigitOrPeriodOrSign(val.charAt(0))
204         || !isDigitOrPeriodOrSign(val.charAt(val.length()-1)) )
205             throw new NumberFormatException JavaDoc(val);
206
207
208         // these screening process is necessary due to the wobble of Float.valueOf method
209
return Double.parseDouble(val);
210     }
211
212     public boolean parseBoolean(String JavaDoc lexicalXSDBoolean) {
213         return _parseBoolean(lexicalXSDBoolean);
214     }
215
216     public static boolean _parseBoolean(CharSequence JavaDoc literal) {
217         int i=0;
218         int len = literal.length();
219         char ch;
220         do {
221             ch = literal.charAt(i++);
222         } while(WhiteSpaceProcessor.isWhiteSpace(ch) && i<len);
223
224         // if we are strict about errors, check i==len. and report an error
225

226         if( ch=='t' || ch=='1' ) return true;
227         if( ch=='f' || ch=='0' ) return false;
228         return false;
229     }
230
231     public String JavaDoc printBoolean(boolean val) {
232         return val?"true":"false";
233     }
234     public static String JavaDoc _printBoolean(boolean val) {
235         return val?"true":"false";
236     }
237
238     public byte parseByte(String JavaDoc lexicalXSDByte) {
239         return _parseByte(lexicalXSDByte);
240     }
241
242     public static byte _parseByte(CharSequence JavaDoc literal) {
243         return (byte)_parseInt(literal);
244     }
245
246     public String JavaDoc printByte(byte val) {
247         return _printByte(val);
248     }
249
250     public static String JavaDoc _printByte(byte val) {
251         return String.valueOf(val);
252     }
253
254     public QName JavaDoc parseQName(String JavaDoc lexicalXSDQName, NamespaceContext JavaDoc nsc) {
255         return _parseQName(lexicalXSDQName,nsc);
256     }
257
258     /**
259      * @return null if fails to convert.
260      */

261     public static QName JavaDoc _parseQName(CharSequence JavaDoc text, NamespaceContext JavaDoc nsc) {
262         int length = text.length();
263
264         // trim whitespace
265
int start=0;
266         while(start<length && WhiteSpaceProcessor.isWhiteSpace(text.charAt(start)))
267             start++;
268
269         int end = length;
270         while(end>start && WhiteSpaceProcessor.isWhiteSpace(text.charAt(end-1)))
271             end--;
272
273         if(end==start)
274             throw new IllegalArgumentException JavaDoc("input is empty");
275
276
277         String JavaDoc uri;
278         String JavaDoc localPart;
279         String JavaDoc prefix;
280
281         // search ':'
282
int idx=start+1; // no point in searching the first char. that's not valid.
283
while(idx<end && text.charAt(idx)!=':' )
284             idx++;
285
286         if( idx==end ) {
287             uri = nsc.getNamespaceURI("");
288             localPart = text.subSequence(start,end).toString();
289             prefix = "";
290         } else {
291             // Prefix exists, check everything
292
prefix = text.subSequence(start,idx).toString();
293             localPart = text.subSequence(idx+1,end).toString();
294             uri = nsc.getNamespaceURI(prefix);
295             // uri can never be null according to javadoc,
296
// but some users reported that there are implementations that return null.
297
if(uri==null || uri.length()==0) // crap. the NamespaceContext interface is broken.
298
// error: unbound prefix
299
throw new IllegalArgumentException JavaDoc("prefix "+prefix+" is not bound to a namespace");
300         }
301
302         return new QName JavaDoc(uri,localPart,prefix);
303     }
304
305     public Calendar JavaDoc parseDateTime(String JavaDoc lexicalXSDDateTime) {
306         return _parseDateTime(lexicalXSDDateTime);
307     }
308
309     public static GregorianCalendar JavaDoc _parseDateTime(CharSequence JavaDoc s) {
310         String JavaDoc val = WhiteSpaceProcessor.trim(s).toString();
311         return datatypeFactory.newXMLGregorianCalendar(val).toGregorianCalendar();
312     }
313
314     public String JavaDoc printDateTime(Calendar JavaDoc val) {
315         return _printDateTime(val);
316     }
317
318     public static String JavaDoc _printDateTime(Calendar JavaDoc val) {
319         return CalendarFormatter.doFormat("%Y-%M-%DT%h:%m:%s%z",val);
320     }
321
322     public byte[] parseBase64Binary(String JavaDoc lexicalXSDBase64Binary) {
323         return _parseBase64Binary(lexicalXSDBase64Binary);
324     }
325
326
327     public byte[] parseHexBinary(String JavaDoc s) {
328         final int len = s.length();
329
330         // "111" is not a valid hex encoding.
331
if( len%2 != 0 )
332             throw new IllegalArgumentException JavaDoc("hexBinary needs to be even-length: "+s);
333
334         byte[] out = new byte[len/2];
335
336         for( int i=0; i<len; i+=2 ) {
337             int h = hexToBin(s.charAt(i ));
338             int l = hexToBin(s.charAt(i+1));
339             if( h==-1 || l==-1 )
340                 throw new IllegalArgumentException JavaDoc("contains illegal character for hexBinary: "+s);
341
342             out[i/2] = (byte)(h*16+l);
343         }
344
345         return out;
346     }
347
348     private static int hexToBin( char ch ) {
349         if( '0'<=ch && ch<='9' ) return ch-'0';
350         if( 'A'<=ch && ch<='F' ) return ch-'A'+10;
351         if( 'a'<=ch && ch<='f' ) return ch-'a'+10;
352         return -1;
353     }
354
355     private static final char[] hexCode = "0123456789ABCDEF".toCharArray();
356
357     public String JavaDoc printHexBinary(byte[] data) {
358         StringBuilder JavaDoc r = new StringBuilder JavaDoc(data.length*2);
359         for ( byte b : data) {
360             r.append(hexCode[(b >> 4) & 0xF]);
361             r.append(hexCode[(b & 0xF)]);
362         }
363         return r.toString();
364     }
365
366
367     public long parseUnsignedInt(String JavaDoc lexicalXSDUnsignedInt) {
368         return _parseLong(lexicalXSDUnsignedInt);
369     }
370
371     public String JavaDoc printUnsignedInt(long val) {
372         return _printLong(val);
373     }
374
375     public int parseUnsignedShort(String JavaDoc lexicalXSDUnsignedShort) {
376         return _parseInt(lexicalXSDUnsignedShort);
377     }
378
379     public Calendar JavaDoc parseTime(String JavaDoc lexicalXSDTime) {
380         return datatypeFactory.newXMLGregorianCalendar(lexicalXSDTime).toGregorianCalendar();
381     }
382
383     public String JavaDoc printTime(Calendar JavaDoc val) {
384         return CalendarFormatter.doFormat("%h:%m:%s%z",val);
385     }
386
387     public Calendar JavaDoc parseDate(String JavaDoc lexicalXSDDate) {
388         return datatypeFactory.newXMLGregorianCalendar(lexicalXSDDate).toGregorianCalendar();
389     }
390
391     public String JavaDoc printDate(Calendar JavaDoc val) {
392
393         return CalendarFormatter.doFormat((new StringBuilder JavaDoc("%Y-%M-%D").append("%z")).toString(),val);
394     }
395
396     public String JavaDoc parseAnySimpleType(String JavaDoc lexicalXSDAnySimpleType) {
397         return lexicalXSDAnySimpleType;
398 // return (String)SimpleURType.theInstance._createValue( lexicalXSDAnySimpleType, null );
399
}
400
401     public String JavaDoc printString(String JavaDoc val) {
402 // return StringType.theInstance.convertToLexicalValue( val, null );
403
return val;
404     }
405
406
407     public String JavaDoc printInt(int val) {
408         return _printInt(val);
409     }
410
411     public static String JavaDoc _printInt(int val) {
412         return String.valueOf(val);
413     }
414
415     public String JavaDoc printLong(long val) {
416         return _printLong(val);
417     }
418
419     public static String JavaDoc _printLong(long val) {
420         return String.valueOf(val);
421     }
422
423     public String JavaDoc printDecimal(BigDecimal JavaDoc val) {
424         return _printDecimal(val);
425     }
426
427     public static String JavaDoc _printDecimal(BigDecimal JavaDoc val) {
428         return val.toPlainString();
429     }
430
431     public String JavaDoc printDouble(double v) {
432         return _printDouble(v);
433     }
434
435     public static String JavaDoc _printDouble(double v) {
436         if( v==Double.NaN ) return "NaN";
437         if( v==Double.POSITIVE_INFINITY ) return "INF";
438         if( v==Double.NEGATIVE_INFINITY ) return "-INF";
439         return String.valueOf(v);
440     }
441
442     public String JavaDoc printQName(QName JavaDoc val, NamespaceContext JavaDoc nsc) {
443         return _printQName(val,nsc);
444     }
445
446     public static String JavaDoc _printQName(QName JavaDoc val, NamespaceContext JavaDoc nsc) {
447         // Double-check
448
String JavaDoc qname;
449         String JavaDoc prefix = nsc.getPrefix( val.getNamespaceURI() );
450         String JavaDoc localPart = val.getLocalPart();
451
452         if( prefix == null || prefix.length()==0 ) { // be defensive
453
qname = localPart;
454         } else {
455             qname = prefix + ':' + localPart;
456         }
457
458         return qname;
459     }
460
461     public String JavaDoc printBase64Binary(byte[] val) {
462         return _printBase64Binary(val);
463     }
464
465     public String JavaDoc printUnsignedShort(int val) {
466         return String.valueOf(val);
467     }
468
469     public String JavaDoc printAnySimpleType(String JavaDoc val) {
470         return val;
471     }
472
473
474     /**
475      * Just return the string passed as a parameter but
476      * installs an instance of this class as the DatatypeConverter
477      * implementation. Used from static fixed value initializers.
478      */

479     public static String JavaDoc installHook( String JavaDoc s ) {
480         DatatypeConverter.setDatatypeConverter(theInstance);
481         return s;
482     }
483
484
485
486
487 // base64 decoder
488
//====================================
489

490     private static final byte[] decodeMap = initDecodeMap();
491     private static final byte PADDING = 127;
492
493     private static byte[] initDecodeMap() {
494         byte[] map = new byte[128];
495         int i;
496         for( i=0; i<128; i++ ) map[i] = -1;
497
498         for( i='A'; i<='Z'; i++ ) map[i] = (byte)(i-'A');
499         for( i='a'; i<='z'; i++ ) map[i] = (byte)(i-'a'+26);
500         for( i='0'; i<='9'; i++ ) map[i] = (byte)(i-'0'+52);
501         map['+'] = 62;
502         map['/'] = 63;
503         map['='] = PADDING;
504
505         return map;
506     }
507
508     /**
509      * computes the length of binary data speculatively.
510      *
511      * <p>
512      * Our requirement is to create byte[] of the exact length to store the binary data.
513      * If we do this in a straight-forward way, it takes two passes over the data.
514      * Experiments show that this is a non-trivial overhead (35% or so is spent on
515      * the first pass in calculating the length.)
516      *
517      * <p>
518      * So the approach here is that we compute the length speculatively, without looking
519      * at the whole contents. The obtained speculative value is never less than the
520      * actual length of the binary data, but it may be bigger. So if the speculation
521      * goes wrong, we'll pay the cost of reallocation and buffer copying.
522      *
523      * <p>
524      * If the base64 text is tightly packed with no indentation nor illegal char
525      * (like what most web services produce), then the speculation of this method
526      * will be correct, so we get the performance benefit.
527      */

528     private static int guessLength( String JavaDoc text ) {
529         final int len = text.length();
530
531         // compute the tail '=' chars
532
int j=len-1;
533         for(; j>=0; j-- ) {
534             byte code = decodeMap[text.charAt(j)];
535             if(code==PADDING)
536                 continue;
537             if(code==-1)
538                 // most likely this base64 text is indented. go with the upper bound
539
return text.length()/4*3;
540             break;
541         }
542
543         j++; // text.charAt(j) is now at some base64 char, so +1 to make it the size
544
int padSize = len-j;
545         if(padSize >2) // something is wrong with base64. be safe and go with the upper bound
546
return text.length()/4*3;
547
548         // so far this base64 looks like it's unindented tightly packed base64.
549
// take a chance and create an array with the expected size
550
return text.length()/4*3-padSize;
551     }
552
553     /**
554      * @param text
555      * base64Binary data is likely to be long, and decoding requires
556      * each character to be accessed twice (once for counting length, another
557      * for decoding.)
558      *
559      * A benchmark showed that taking {@link String} is faster, presumably
560      * because JIT can inline a lot of string access (with data of 1K chars, it was twice as fast)
561      */

562     public static byte[] _parseBase64Binary(String JavaDoc text) {
563         final int buflen = guessLength(text);
564         final byte[] out = new byte[buflen];
565         int o=0;
566
567         final int len = text.length();
568         int i;
569
570         final byte[] quadruplet = new byte[4];
571         int q=0;
572
573         // convert each quadruplet to three bytes.
574
for( i=0; i<len; i++ ) {
575             char ch = text.charAt(i);
576             byte v = decodeMap[ch];
577
578             if( v!=-1 )
579                 quadruplet[q++] = v;
580
581             if(q==4) {
582                 // quadruplet is now filled.
583
out[o++] = (byte)((quadruplet[0]<<2)|(quadruplet[1]>>4));
584                 if( quadruplet[2]!=PADDING )
585                     out[o++] = (byte)((quadruplet[1]<<4)|(quadruplet[2]>>2));
586                 if( quadruplet[3]!=PADDING )
587                     out[o++] = (byte)((quadruplet[2]<<6)|(quadruplet[3]));
588                 q=0;
589             }
590         }
591
592         if(buflen==o) // speculation worked out to be OK
593
return out;
594
595         // we overestimated, so need to create a new buffer
596
byte[] nb = new byte[o];
597         System.arraycopy(out,0,nb,0,o);
598         return nb;
599     }
600
601     private static final char[] encodeMap = initEncodeMap();
602
603     private static char[] initEncodeMap() {
604         char[] map = new char[64];
605         int i;
606         for( i= 0; i<26; i++ ) map[i] = (char)('A'+i);
607         for( i=26; i<52; i++ ) map[i] = (char)('a'+(i-26));
608         for( i=52; i<62; i++ ) map[i] = (char)('0'+(i-52));
609         map[62] = '+';
610         map[63] = '/';
611
612         return map;
613     }
614
615     public static char encode( int i ) {
616         return encodeMap[i&0x3F];
617     }
618
619     public static byte encodeByte( int i ) {
620         return (byte)encodeMap[i&0x3F];
621     }
622
623     public static String JavaDoc _printBase64Binary(byte[] input) {
624         return _printBase64Binary(input, 0, input.length);
625     }
626     public static String JavaDoc _printBase64Binary(byte[] input, int offset, int len) {
627         char[] buf = new char[((len+2)/3)*4];
628         int ptr = _printBase64Binary(input,offset,len,buf,0);
629         assert ptr==buf.length;
630         return new String JavaDoc(buf);
631     }
632
633     /**
634      * Encodes a byte array into a char array by doing base64 encoding.
635      *
636      * The caller must supply a big enough buffer.
637      *
638      * @return
639      * the value of {@code ptr+((len+2)/3)*4}, which is the new offset
640      * in the output buffer where the further bytes should be placed.
641      */

642     public static int _printBase64Binary(byte[] input, int offset, int len, char[] buf, int ptr) {
643         for( int i=offset; i<len; i+=3 ) {
644             switch( len-i ) {
645             case 1:
646                 buf[ptr++] = encode(input[i]>>2);
647                 buf[ptr++] = encode(((input[i])&0x3)<<4);
648                 buf[ptr++] = '=';
649                 buf[ptr++] = '=';
650                 break;
651             case 2:
652                 buf[ptr++] = encode(input[i]>>2);
653                 buf[ptr++] = encode(
654                             ((input[i]&0x3)<<4) |
655                             ((input[i+1]>>4)&0xF));
656                 buf[ptr++] = encode((input[i+1]&0xF)<<2);
657                 buf[ptr++] = '=';
658                 break;
659             default:
660                 buf[ptr++] = encode(input[i]>>2);
661                 buf[ptr++] = encode(
662                             ((input[i]&0x3)<<4) |
663                             ((input[i+1]>>4)&0xF));
664                 buf[ptr++] = encode(
665                             ((input[i+1]&0xF)<<2)|
666                             ((input[i+2]>>6)&0x3));
667                 buf[ptr++] = encode(input[i+2]&0x3F);
668                 break;
669             }
670         }
671         return ptr;
672     }
673
674     /**
675      * Encodes a byte array into another byte array by first doing base64 encoding
676      * then encoding the result in ASCII.
677      *
678      * The caller must supply a big enough buffer.
679      *
680      * @return
681      * the value of {@code ptr+((len+2)/3)*4}, which is the new offset
682      * in the output buffer where the further bytes should be placed.
683      */

684     public static int _printBase64Binary(byte[] input, int offset, int len, byte[] out, int ptr) {
685         byte[] buf = out;
686         int max = len+offset;
687         for( int i=offset; i<max; i+=3 ) {
688             switch( max-i ) {
689             case 1:
690                 buf[ptr++] = encodeByte(input[i]>>2);
691                 buf[ptr++] = encodeByte(((input[i])&0x3)<<4);
692                 buf[ptr++] = '=';
693                 buf[ptr++] = '=';
694                 break;
695             case 2:
696                 buf[ptr++] = encodeByte(input[i]>>2);
697                 buf[ptr++] = encodeByte(
698                             ((input[i]&0x3)<<4) |
699                             ((input[i+1]>>4)&0xF));
700                 buf[ptr++] = encodeByte((input[i+1]&0xF)<<2);
701                 buf[ptr++] = '=';
702                 break;
703             default:
704                 buf[ptr++] = encodeByte(input[i]>>2);
705                 buf[ptr++] = encodeByte(
706                             ((input[i]&0x3)<<4) |
707                             ((input[i+1]>>4)&0xF));
708                 buf[ptr++] = encodeByte(
709                             ((input[i+1]&0xF)<<2)|
710                             ((input[i+2]>>6)&0x3));
711                 buf[ptr++] = encodeByte(input[i+2]&0x3F);
712                 break;
713             }
714         }
715
716         return ptr;
717     }
718
719     private static CharSequence JavaDoc removeOptionalPlus(CharSequence JavaDoc s) {
720         int len = s.length();
721
722         if(len<=1 || s.charAt(0)!='+') return s;
723
724         s = s.subSequence(1,len);
725         char ch = s.charAt(0);
726         if('0'<=ch && ch<='9') return s;
727         if('.'==ch ) return s;
728
729         throw new NumberFormatException JavaDoc();
730     }
731
732     private static boolean isDigitOrPeriodOrSign( char ch ) {
733         if( '0'<=ch && ch<='9' ) return true;
734         if( ch=='+' || ch=='-' || ch=='.' ) return true;
735         return false;
736     }
737
738     private static final DatatypeFactory JavaDoc datatypeFactory;
739
740     static {
741         try {
742             datatypeFactory = DatatypeFactory.newInstance();
743         } catch (DatatypeConfigurationException JavaDoc e) {
744             throw new Error JavaDoc(e);
745         }
746     }
747
748
749     private static final class CalendarFormatter {
750         public static String JavaDoc doFormat( String JavaDoc format, Calendar JavaDoc cal ) throws IllegalArgumentException JavaDoc {
751             int fidx = 0;
752             int flen = format.length();
753             StringBuilder JavaDoc buf = new StringBuilder JavaDoc();
754
755             while(fidx<flen) {
756                 char fch = format.charAt(fidx++);
757
758                 if(fch!='%') { // not a meta character
759
buf.append(fch);
760                     continue;
761                 }
762
763                 // seen meta character. we don't do error check against the format
764
switch (format.charAt(fidx++)) {
765                 case 'Y' : // year
766
formatYear(cal, buf);
767                     break;
768
769                 case 'M' : // month
770
formatMonth(cal, buf);
771                     break;
772
773                 case 'D' : // days
774
formatDays(cal, buf);
775                     break;
776
777                 case 'h' : // hours
778
formatHours(cal, buf);
779                     break;
780
781                 case 'm' : // minutes
782
formatMinutes(cal, buf);
783                     break;
784
785                 case 's' : // parse seconds.
786
formatSeconds(cal, buf);
787                     break;
788
789                 case 'z' : // time zone
790
formatTimeZone(cal,buf);
791                     break;
792
793                 default :
794                     // illegal meta character. impossible.
795
throw new InternalError JavaDoc();
796                 }
797             }
798
799             return buf.toString();
800         }
801
802
803         private static void formatYear(Calendar JavaDoc cal, StringBuilder JavaDoc buf) {
804             int year = cal.get(Calendar.YEAR);
805
806             String JavaDoc s;
807             if (year <= 0) // negative value
808
s = Integer.toString(1 - year);
809             else // positive value
810
s = Integer.toString(year);
811
812             while (s.length() < 4)
813                 s = '0' + s;
814             if (year <= 0)
815                 s = '-' + s;
816
817             buf.append(s);
818         }
819
820         private static void formatMonth(Calendar JavaDoc cal, StringBuilder JavaDoc buf) {
821             formatTwoDigits(cal.get(Calendar.MONTH)+1,buf);
822         }
823
824         private static void formatDays(Calendar JavaDoc cal, StringBuilder JavaDoc buf) {
825             formatTwoDigits(cal.get(Calendar.DAY_OF_MONTH),buf);
826         }
827
828         private static void formatHours(Calendar JavaDoc cal, StringBuilder JavaDoc buf) {
829             formatTwoDigits(cal.get(Calendar.HOUR_OF_DAY),buf);
830         }
831
832         private static void formatMinutes(Calendar JavaDoc cal, StringBuilder JavaDoc buf) {
833             formatTwoDigits(cal.get(Calendar.MINUTE),buf);
834         }
835
836         private static void formatSeconds(Calendar JavaDoc cal, StringBuilder JavaDoc buf) {
837             formatTwoDigits(cal.get(Calendar.SECOND),buf);
838             if (cal.isSet(Calendar.MILLISECOND)) { // milliseconds
839
int n = cal.get(Calendar.MILLISECOND);
840                 if(n!=0) {
841                     String JavaDoc ms = Integer.toString(n);
842                     while (ms.length() < 3)
843                         ms = '0' + ms; // left 0 paddings.
844

845                     buf.append('.');
846                     buf.append(ms);
847                 }
848             }
849         }
850
851         /** formats time zone specifier. */
852         private static void formatTimeZone(Calendar JavaDoc cal,StringBuilder JavaDoc buf) {
853             TimeZone JavaDoc tz = cal.getTimeZone();
854
855             if (tz == null) return;
856
857             // otherwise print out normally.
858
int offset;
859             if (tz.inDaylightTime(cal.getTime())) {
860                 offset = tz.getRawOffset() + (tz.useDaylightTime()?3600000:0);
861             } else {
862                 offset = tz.getRawOffset();
863             }
864
865             if(offset==0) {
866                 buf.append('Z');
867                 return;
868             }
869
870             if (offset >= 0)
871                 buf.append('+');
872             else {
873                 buf.append('-');
874                 offset *= -1;
875             }
876
877             offset /= 60 * 1000; // offset is in milli-seconds
878

879             formatTwoDigits(offset / 60, buf);
880             buf.append(':');
881             formatTwoDigits(offset % 60, buf);
882         }
883
884         /** formats Integer into two-character-wide string. */
885         private static void formatTwoDigits(int n,StringBuilder JavaDoc buf) {
886             // n is always non-negative.
887
if (n < 10) buf.append('0');
888             buf.append(n);
889         }
890     }
891 }
892
Popular Tags