]> gitweb.fperrin.net Git - Dictionary.git/blob - jars/icu4j-52_1/main/classes/core/src/com/ibm/icu/impl/Punycode.java
Clean up imports.
[Dictionary.git] / jars / icu4j-52_1 / main / classes / core / src / com / ibm / icu / impl / Punycode.java
1 /*
2  *******************************************************************************
3  * Copyright (C) 2003-2010, International Business Machines Corporation and    *
4  * others. All Rights Reserved.                                                *
5  *******************************************************************************
6  */
7 package com.ibm.icu.impl;
8
9 import com.ibm.icu.lang.UCharacter;
10 import com.ibm.icu.text.StringPrepParseException;
11 import com.ibm.icu.text.UTF16;
12
13 /**
14  * Ported code from ICU punycode.c 
15  * @author ram
16  */
17
18 /* Package Private class */
19 public final class Punycode {
20
21     /* Punycode parameters for Bootstring */
22     private static final int BASE           = 36;
23     private static final int TMIN           = 1;
24     private static final int TMAX           = 26;
25     private static final int SKEW           = 38;
26     private static final int DAMP           = 700;
27     private static final int INITIAL_BIAS   = 72;
28     private static final int INITIAL_N      = 0x80;
29     
30     /* "Basic" Unicode/ASCII code points */
31     private static final int HYPHEN         = 0x2d;
32     private static final int DELIMITER      = HYPHEN;
33     
34     private static final int ZERO           = 0x30;
35     //private static final int NINE           = 0x39;
36     
37     private static final int SMALL_A        = 0x61;
38     private static final int SMALL_Z        = 0x7a;
39     
40     private static final int CAPITAL_A      = 0x41;
41     private static final int CAPITAL_Z      = 0x5a;
42     private static final int MAX_CP_COUNT   = 200;
43     //private static final int UINT_MAGIC     = 0x80000000;
44     //private static final long ULONG_MAGIC   = 0x8000000000000000L;
45     
46     private static int adaptBias(int delta, int length, boolean firstTime){
47         if(firstTime){
48             delta /=DAMP;
49         }else{
50             delta /=  2;
51         }
52         delta += delta/length;
53
54         int count=0;
55         for(; delta>((BASE-TMIN)*TMAX)/2; count+=BASE) {
56             delta/=(BASE-TMIN);
57         }
58
59         return count+(((BASE-TMIN+1)*delta)/(delta+SKEW));         
60     }
61
62     /**
63      * basicToDigit[] contains the numeric value of a basic code
64      * point (for use in representing integers) in the range 0 to
65      * BASE-1, or -1 if b is does not represent a value.
66      */
67     static final int[]    basicToDigit= new int[]{
68         -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
69         -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
70     
71         -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
72         26, 27, 28, 29, 30, 31, 32, 33, 34, 35, -1, -1, -1, -1, -1, -1,
73     
74         -1,  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14,
75         15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1,
76     
77         -1,  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14,
78         15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1,
79     
80         -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
81         -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
82     
83         -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
84         -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
85     
86         -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
87         -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
88     
89         -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
90         -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
91     };
92
93     ///CLOVER:OFF
94     private static char asciiCaseMap(char b, boolean uppercase) {
95         if(uppercase) {
96             if(SMALL_A<=b && b<=SMALL_Z) {
97                 b-=(SMALL_A-CAPITAL_A);
98             }
99         } else {
100             if(CAPITAL_A<=b && b<=CAPITAL_Z) {
101                 b+=(SMALL_A-CAPITAL_A);
102             }
103         }
104         return b;
105     }    
106     ///CLOVER:ON
107     /**
108      * digitToBasic() returns the basic code point whose value
109      * (when used for representing integers) is d, which must be in the
110      * range 0 to BASE-1. The lowercase form is used unless the uppercase flag is
111      * nonzero, in which case the uppercase form is used.
112      */
113     private static char digitToBasic(int digit, boolean uppercase) {
114         /*  0..25 map to ASCII a..z or A..Z */
115         /* 26..35 map to ASCII 0..9         */
116         if(digit<26) {
117             if(uppercase) {
118                 return (char)(CAPITAL_A+digit);
119             } else {
120                 return (char)(SMALL_A+digit);
121             }
122         } else {
123             return (char)((ZERO-26)+digit);
124         }
125     }
126     /**
127      * Converts Unicode to Punycode.
128      * The input string must not contain single, unpaired surrogates.
129      * The output will be represented as an array of ASCII code points.
130      * 
131      * @param src The source of the String Buffer passed.
132      * @param caseFlags The boolean array of case flags.
133      * @return An array of ASCII code points.
134      */
135     public static StringBuilder encode(CharSequence src, boolean[] caseFlags) throws StringPrepParseException{
136         
137         int[] cpBuffer = new int[MAX_CP_COUNT];
138         int n, delta, handledCPCount, basicLength, destLength, bias, j, m, q, k, t, srcCPCount;
139         char c, c2;
140         int srcLength = src.length();
141         int destCapacity = MAX_CP_COUNT;
142         char[] dest = new char[destCapacity];
143         StringBuilder result = new StringBuilder();
144         /*
145          * Handle the basic code points and
146          * convert extended ones to UTF-32 in cpBuffer (caseFlag in sign bit):
147          */
148         srcCPCount=destLength=0;
149         
150         for(j=0; j<srcLength; ++j) {
151             if(srcCPCount==MAX_CP_COUNT) {
152                 /* too many input code points */
153                 throw new IndexOutOfBoundsException();
154             }
155             c=src.charAt(j);
156             if(isBasic(c)) {
157                 if(destLength<destCapacity) {
158                     cpBuffer[srcCPCount++]=0;
159                     dest[destLength]=
160                         caseFlags!=null ?
161                             asciiCaseMap(c, caseFlags[j]) :
162                             c;
163                 }
164                 ++destLength;
165             } else {
166                 n=((caseFlags!=null && caseFlags[j])? 1 : 0)<<31L;
167                 if(!UTF16.isSurrogate(c)) {
168                     n|=c;
169                 } else if(UTF16.isLeadSurrogate(c) && (j+1)<srcLength && UTF16.isTrailSurrogate(c2=src.charAt(j+1))) {
170                     ++j;
171                     
172                     n|=UCharacter.getCodePoint(c, c2);
173                 } else {
174                     /* error: unmatched surrogate */
175                     throw new StringPrepParseException("Illegal char found",StringPrepParseException.ILLEGAL_CHAR_FOUND);
176                 }
177                 cpBuffer[srcCPCount++]=n;
178             }
179         }
180
181         /* Finish the basic string - if it is not empty - with a delimiter. */
182         basicLength=destLength;
183         if(basicLength>0) {
184             if(destLength<destCapacity) {
185                 dest[destLength]=DELIMITER;
186             }
187             ++destLength;
188         }
189
190         /*
191          * handledCPCount is the number of code points that have been handled
192          * basicLength is the number of basic code points
193          * destLength is the number of chars that have been output
194          */
195
196         /* Initialize the state: */
197         n=INITIAL_N;
198         delta=0;
199         bias=INITIAL_BIAS;
200
201         /* Main encoding loop: */
202         for(handledCPCount=basicLength; handledCPCount<srcCPCount; /* no op */) {
203             /*
204              * All non-basic code points < n have been handled already.
205              * Find the next larger one:
206              */
207             for(m=0x7fffffff, j=0; j<srcCPCount; ++j) {
208                 q=cpBuffer[j]&0x7fffffff; /* remove case flag from the sign bit */
209                 if(n<=q && q<m) {
210                     m=q;
211                 }
212             }
213
214             /*
215              * Increase delta enough to advance the decoder's
216              * <n,i> state to <m,0>, but guard against overflow:
217              */
218             if(m-n>(0x7fffffff-MAX_CP_COUNT-delta)/(handledCPCount+1)) {
219                 throw new IllegalStateException("Internal program error");
220             }
221             delta+=(m-n)*(handledCPCount+1);
222             n=m;
223
224             /* Encode a sequence of same code points n */
225             for(j=0; j<srcCPCount; ++j) {
226                 q=cpBuffer[j]&0x7fffffff; /* remove case flag from the sign bit */
227                 if(q<n) {
228                     ++delta;
229                 } else if(q==n) {
230                     /* Represent delta as a generalized variable-length integer: */
231                     for(q=delta, k=BASE; /* no condition */; k+=BASE) {
232
233                         /** RAM: comment out the old code for conformance with draft-ietf-idn-punycode-03.txt   
234
235                         t=k-bias;
236                         if(t<TMIN) {
237                             t=TMIN;
238                         } else if(t>TMAX) {
239                             t=TMAX;
240                         }
241                         */
242                     
243                         t=k-bias;
244                         if(t<TMIN) {
245                             t=TMIN;
246                         } else if(k>=(bias+TMAX)) {
247                             t=TMAX;
248                         }
249
250                         if(q<t) {
251                             break;
252                         }
253
254                         if(destLength<destCapacity) {
255                             dest[destLength++]=digitToBasic(t+(q-t)%(BASE-t), false);
256                         }
257                         q=(q-t)/(BASE-t);
258                     }
259
260                     if(destLength<destCapacity) {
261                         dest[destLength++]=digitToBasic(q, (cpBuffer[j]<0));
262                     }
263                     bias=adaptBias(delta, handledCPCount+1,(handledCPCount==basicLength));
264                     delta=0;
265                     ++handledCPCount;
266                 }
267             }
268
269             ++delta;
270             ++n;
271         }
272
273         return result.append(dest, 0, destLength);
274     }
275     
276     private static boolean isBasic(int ch){
277         return (ch < INITIAL_N);
278     }
279     ///CLOVER:OFF
280     private static boolean isBasicUpperCase(int ch){
281         return( CAPITAL_A<=ch && ch >= CAPITAL_Z);
282     }
283     ///CLOVER:ON
284     private static boolean isSurrogate(int ch){
285         return (((ch)&0xfffff800)==0xd800);
286     }
287     /**
288      * Converts Punycode to Unicode.
289      * The Unicode string will be at most as long as the Punycode string.
290      * 
291      * @param src The source of the string buffer being passed.
292      * @param caseFlags The array of boolean case flags.
293      * @return StringBuilder string.
294      */
295     public static StringBuilder decode(CharSequence src, boolean[] caseFlags) 
296                                throws StringPrepParseException{
297         int srcLength = src.length();
298         StringBuilder result = new StringBuilder();
299         int n, destLength, i, bias, basicLength, j, in, oldi, w, k, digit, t,
300                 destCPCount, firstSupplementaryIndex, cpLength;
301         char b;
302         int destCapacity = MAX_CP_COUNT;
303         char[] dest = new char[destCapacity];
304
305         /*
306          * Handle the basic code points:
307          * Let basicLength be the number of input code points
308          * before the last delimiter, or 0 if there is none,
309          * then copy the first basicLength code points to the output.
310          *
311          * The two following loops iterate backward.
312          */
313         for(j=srcLength; j>0;) {
314             if(src.charAt(--j)==DELIMITER) {
315                 break;
316             }
317         }
318         destLength=basicLength=destCPCount=j;
319
320         while(j>0) {
321             b=src.charAt(--j);
322             if(!isBasic(b)) {
323                 throw new StringPrepParseException("Illegal char found", StringPrepParseException.INVALID_CHAR_FOUND);
324             }
325
326             if(j<destCapacity) {
327                 dest[j]= b;
328
329                 if(caseFlags!=null) {
330                     caseFlags[j]=isBasicUpperCase(b);
331                 }
332             }
333         }
334
335         /* Initialize the state: */
336         n=INITIAL_N;
337         i=0;
338         bias=INITIAL_BIAS;
339         firstSupplementaryIndex=1000000000;
340
341         /*
342          * Main decoding loop:
343          * Start just after the last delimiter if any
344          * basic code points were copied; start at the beginning otherwise.
345          */
346         for(in=basicLength>0 ? basicLength+1 : 0; in<srcLength; /* no op */) {
347             /*
348              * in is the index of the next character to be consumed, and
349              * destCPCount is the number of code points in the output array.
350              *
351              * Decode a generalized variable-length integer into delta,
352              * which gets added to i.  The overflow checking is easier
353              * if we increase i as we go, then subtract off its starting
354              * value at the end to obtain delta.
355              */
356             for(oldi=i, w=1, k=BASE; /* no condition */; k+=BASE) {
357                 if(in>=srcLength) {
358                     throw new StringPrepParseException("Illegal char found", StringPrepParseException.ILLEGAL_CHAR_FOUND);
359                 }
360
361                 digit=basicToDigit[src.charAt(in++) & 0xFF];
362                 if(digit<0) {
363                     throw new StringPrepParseException("Invalid char found", StringPrepParseException.INVALID_CHAR_FOUND);
364                 }
365                 if(digit>(0x7fffffff-i)/w) {
366                     /* integer overflow */
367                     throw new StringPrepParseException("Illegal char found", StringPrepParseException.ILLEGAL_CHAR_FOUND);
368                 }
369
370                 i+=digit*w;
371                 t=k-bias;
372                 if(t<TMIN) {
373                     t=TMIN;
374                 } else if(k>=(bias+TMAX)) {
375                     t=TMAX;
376                 }
377                 if(digit<t) {
378                     break;
379                 }
380
381                 if(w>0x7fffffff/(BASE-t)) {
382                     /* integer overflow */
383                     throw new StringPrepParseException("Illegal char found", StringPrepParseException.ILLEGAL_CHAR_FOUND);
384                 }
385                 w*=BASE-t;
386             }
387
388             /*
389              * Modification from sample code:
390              * Increments destCPCount here,
391              * where needed instead of in for() loop tail.
392              */
393             ++destCPCount;
394             bias=adaptBias(i-oldi, destCPCount, (oldi==0));
395
396             /*
397              * i was supposed to wrap around from (incremented) destCPCount to 0,
398              * incrementing n each time, so we'll fix that now:
399              */
400             if(i/destCPCount>(0x7fffffff-n)) {
401                 /* integer overflow */
402                 throw new StringPrepParseException("Illegal char found", StringPrepParseException.ILLEGAL_CHAR_FOUND);
403             }
404
405             n+=i/destCPCount;
406             i%=destCPCount;
407             /* not needed for Punycode: */
408             /* if (decode_digit(n) <= BASE) return punycode_invalid_input; */
409
410             if(n>0x10ffff || isSurrogate(n)) {
411                 /* Unicode code point overflow */
412                 throw new StringPrepParseException("Illegal char found", StringPrepParseException.ILLEGAL_CHAR_FOUND);
413             }
414
415             /* Insert n at position i of the output: */
416             cpLength=UTF16.getCharCount(n);
417             if((destLength+cpLength)<destCapacity) {
418                 int codeUnitIndex;
419
420                 /*
421                  * Handle indexes when supplementary code points are present.
422                  *
423                  * In almost all cases, there will be only BMP code points before i
424                  * and even in the entire string.
425                  * This is handled with the same efficiency as with UTF-32.
426                  *
427                  * Only the rare cases with supplementary code points are handled
428                  * more slowly - but not too bad since this is an insertion anyway.
429                  */
430                 if(i<=firstSupplementaryIndex) {
431                     codeUnitIndex=i;
432                     if(cpLength>1) {
433                         firstSupplementaryIndex=codeUnitIndex;
434                     } else {
435                         ++firstSupplementaryIndex;
436                     }
437                 } else {
438                     codeUnitIndex=firstSupplementaryIndex;
439                     codeUnitIndex=UTF16.moveCodePointOffset(dest, 0, destLength, codeUnitIndex, i-codeUnitIndex);
440                 }
441
442                 /* use the UChar index codeUnitIndex instead of the code point index i */
443                 if(codeUnitIndex<destLength) {
444                     System.arraycopy(dest, codeUnitIndex,
445                                      dest, codeUnitIndex+cpLength,
446                                     (destLength-codeUnitIndex));
447                     if(caseFlags!=null) {
448                         System.arraycopy(caseFlags, codeUnitIndex,
449                                          caseFlags, codeUnitIndex+cpLength,
450                                          destLength-codeUnitIndex);
451                     }
452                 }
453                 if(cpLength==1) {
454                     /* BMP, insert one code unit */
455                     dest[codeUnitIndex]=(char)n;
456                 } else {
457                     /* supplementary character, insert two code units */
458                     dest[codeUnitIndex]=UTF16.getLeadSurrogate(n);
459                     dest[codeUnitIndex+1]=UTF16.getTrailSurrogate(n);
460                 }
461                 if(caseFlags!=null) {
462                     /* Case of last character determines uppercase flag: */
463                     caseFlags[codeUnitIndex]=isBasicUpperCase(src.charAt(in-1));
464                     if(cpLength==2) {
465                         caseFlags[codeUnitIndex+1]=false;
466                     }
467                 }
468             }
469             destLength+=cpLength;
470             ++i;
471         }
472         result.append(dest, 0, destLength);
473         return result;
474     }
475 }
476