View Javadoc

1   /*
2    * $HeadURL$
3    * $Revision: 1470921 $
4    * $Date: 2013-04-23 12:42:29 +0000 (Tue, 23 Apr 2013) $
5    *
6    * ====================================================================
7    *
8    *  Licensed to the Apache Software Foundation (ASF) under one or more
9    *  contributor license agreements.  See the NOTICE file distributed with
10   *  this work for additional information regarding copyright ownership.
11   *  The ASF licenses this file to You under the Apache License, Version 2.0
12   *  (the "License"); you may not use this file except in compliance with
13   *  the License.  You may obtain a copy of the License at
14   *
15   *      http://www.apache.org/licenses/LICENSE-2.0
16   *
17   *  Unless required by applicable law or agreed to in writing, software
18   *  distributed under the License is distributed on an "AS IS" BASIS,
19   *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20   *  See the License for the specific language governing permissions and
21   *  limitations under the License.
22   * ====================================================================
23   *
24   * This software consists of voluntary contributions made by many
25   * individuals on behalf of the Apache Software Foundation.  For more
26   * information on the Apache Software Foundation, please see
27   * <http://www.apache.org/>.
28   *
29   */
30  
31  package org.apache.http.client.utils;
32  
33  import java.util.StringTokenizer;
34  
35  import org.apache.http.annotation.Immutable;
36  
37  /**
38   * Implementation from pseudo code in RFC 3492.
39   *
40   * @since 4.0
41   */
42  @Immutable
43  public class Rfc3492Idn implements Idn {
44      private static final int base = 36;
45      private static final int tmin = 1;
46      private static final int tmax = 26;
47      private static final int skew = 38;
48      private static final int damp = 700;
49      private static final int initial_bias = 72;
50      private static final int initial_n = 128;
51      private static final char delimiter = '-';
52      private static final String ACE_PREFIX = "xn--";
53  
54      private int adapt(int delta, int numpoints, boolean firsttime) {
55          if (firsttime) delta = delta / damp;
56          else delta = delta / 2;
57          delta = delta + (delta / numpoints);
58          int k = 0;
59          while (delta > ((base - tmin) * tmax) / 2) {
60            delta = delta / (base - tmin);
61            k = k + base;
62          }
63          return k + (((base - tmin + 1) * delta) / (delta + skew));
64      }
65  
66      private int digit(char c) {
67          if ((c >= 'A') && (c <= 'Z')) return (c - 'A');
68          if ((c >= 'a') && (c <= 'z')) return (c - 'a');
69          if ((c >= '0') && (c <= '9')) return (c - '0') + 26;
70          throw new IllegalArgumentException("illegal digit: "+ c);
71      }
72  
73      public String toUnicode(String punycode) {
74          StringBuilder unicode = new StringBuilder(punycode.length());
75          StringTokenizer tok = new StringTokenizer(punycode, ".");
76          while (tok.hasMoreTokens()) {
77              String t = tok.nextToken();
78              if (unicode.length() > 0) unicode.append('.');
79              if (t.startsWith(ACE_PREFIX)) t = decode(t.substring(4));
80              unicode.append(t);
81          }
82          return unicode.toString();
83      }
84  
85      protected String decode(String input) {
86          int n = initial_n;
87          int i = 0;
88          int bias = initial_bias;
89          StringBuilder output = new StringBuilder(input.length());
90          int lastdelim = input.lastIndexOf(delimiter);
91          if (lastdelim != -1) {
92              output.append(input.subSequence(0, lastdelim));
93              input = input.substring(lastdelim + 1);
94          }
95  
96          while (input.length() > 0) {
97              int oldi = i;
98              int w = 1;
99              for (int k = base;; k += base) {
100                 if (input.length() == 0) break;
101                 char c = input.charAt(0);
102                 input = input.substring(1);
103                 int digit = digit(c);
104                 i = i + digit * w; // FIXME fail on overflow
105                 int t;
106                 if (k <= bias + tmin) {
107                     t = tmin;
108                 } else if (k >= bias + tmax) {
109                     t = tmax;
110                 } else {
111                     t = k - bias;
112                 }
113                 if (digit < t) break;
114                 w = w * (base - t); // FIXME fail on overflow
115             }
116             bias = adapt(i - oldi, output.length() + 1, (oldi == 0));
117             n = n + i / (output.length() + 1); // FIXME fail on overflow
118             i = i % (output.length() + 1);
119             // {if n is a basic code point then fail}
120             output.insert(i, (char) n);
121             i++;
122         }
123         return output.toString();
124     }
125 
126 }