2 *******************************************************************************
3 * Copyright (C) 2003-2011, International Business Machines Corporation and *
4 * others. All Rights Reserved. *
5 *******************************************************************************
7 package com.ibm.icu.dev.test.stringprep;
9 import com.ibm.icu.text.StringPrepParseException;
10 import com.ibm.icu.text.UCharacterIterator;
15 * To change the template for this generated type comment go to
16 * Window>Preferences>Java>Code Generation>Code and Comments
18 public class IDNAReference {
20 private static char[] ACE_PREFIX = new char[]{ 0x0078,0x006E,0x002d,0x002d } ;
21 private static final int ACE_PREFIX_LENGTH = 4;
23 private static final int MAX_LABEL_LENGTH = 63;
24 private static final int HYPHEN = 0x002D;
25 private static final int CAPITAL_A = 0x0041;
26 private static final int CAPITAL_Z = 0x005A;
27 private static final int LOWER_CASE_DELTA = 0x0020;
28 private static final int FULL_STOP = 0x002E;
31 public static final int DEFAULT = 0x0000;
32 public static final int ALLOW_UNASSIGNED = 0x0001;
33 public static final int USE_STD3_RULES = 0x0002;
34 public static final NamePrepTransform transform = NamePrepTransform.getInstance();
36 public static boolean isReady() {
37 return transform.isReady();
40 private static boolean startsWithPrefix(StringBuffer src){
41 boolean startsWithPrefix = true;
43 if(src.length() < ACE_PREFIX_LENGTH){
46 for(int i=0; i<ACE_PREFIX_LENGTH;i++){
47 if(toASCIILower(src.charAt(i)) != ACE_PREFIX[i]){
48 startsWithPrefix = false;
51 return startsWithPrefix;
54 private static char toASCIILower(char ch){
55 if(CAPITAL_A <= ch && ch <= CAPITAL_Z){
56 return (char)(ch + LOWER_CASE_DELTA);
61 private static StringBuffer toASCIILower(StringBuffer src){
62 StringBuffer dest = new StringBuffer();
63 for(int i=0; i<src.length();i++){
64 dest.append(toASCIILower(src.charAt(i)));
69 private static int compareCaseInsensitiveASCII(StringBuffer s1, StringBuffer s2){
72 for(int i =0;/* no condition */;i++) {
73 /* If we reach the ends of both strings then they match */
74 if(i == s1.length()) {
81 /* Case-insensitive comparison */
83 rc=toASCIILower(c1)-toASCIILower(c2);
91 private static int getSeparatorIndex(char[] src,int start, int limit){
92 for(; start<limit;start++){
93 if(NamePrepTransform.isLabelSeparator(src[start])){
97 // we have not found the separator just return length
101 private static boolean isLDHChar(int ch){
106 //[\\u002D \\u0030-\\u0039 \\u0041-\\u005A \\u0061-\\u007A]
108 (0x0030 <= ch && ch <= 0x0039) ||
109 (0x0041 <= ch && ch <= 0x005A) ||
110 (0x0061 <= ch && ch <= 0x007A)
117 public static StringBuffer convertToASCII(String src, int options)
118 throws StringPrepParseException{
119 UCharacterIterator iter = UCharacterIterator.getInstance(src);
120 return convertToASCII(iter,options);
122 public static StringBuffer convertToASCII(StringBuffer src, int options)
123 throws StringPrepParseException{
124 UCharacterIterator iter = UCharacterIterator.getInstance(src);
125 return convertToASCII(iter,options);
127 public static StringBuffer convertToASCII(UCharacterIterator srcIter, int options)
128 throws StringPrepParseException{
130 char[] caseFlags = null;
132 // the source contains all ascii codepoints
133 boolean srcIsASCII = true;
134 // assume the source contains all LDH codepoints
135 boolean srcIsLDH = true;
138 boolean useSTD3ASCIIRules = ((options & USE_STD3_RULES) != 0);
142 while((ch = srcIter.next())!= UCharacterIterator.DONE){
148 srcIter.setToStart();
149 StringBuffer processOut = null;
150 // step 2 is performed only if the source contains non ASCII
153 processOut = transform.prepare(srcIter,options);
155 processOut = new StringBuffer(srcIter.getText());
157 int poLen = processOut.length();
159 throw new StringPrepParseException("Found zero length lable after NamePrep.",StringPrepParseException.ZERO_LENGTH_LABEL);
161 StringBuffer dest = new StringBuffer();
163 // reset the variable to verify if output of prepare is ASCII or not
167 for(int j=0;j<poLen;j++ ){
168 ch=processOut.charAt(j);
171 }else if(isLDHChar(ch)==false){
172 // here we do not assemble surrogates
173 // since we know that LDH code points
174 // are in the ASCII range only
180 if(useSTD3ASCIIRules == true){
182 if( srcIsLDH == false /* source contains some non-LDH characters */
183 || processOut.charAt(0) == HYPHEN
184 || processOut.charAt(processOut.length()-1) == HYPHEN){
186 /* populate the parseError struct */
188 throw new StringPrepParseException( "The input does not conform to the STD 3 ASCII rules",
189 StringPrepParseException.STD3_ASCII_RULES_ERROR,
190 processOut.toString(),
191 (failPos>0) ? (failPos-1) : failPos);
192 }else if(processOut.charAt(0) == HYPHEN){
193 throw new StringPrepParseException("The input does not conform to the STD 3 ASCII rules",
194 StringPrepParseException.STD3_ASCII_RULES_ERROR,processOut.toString(),0);
197 throw new StringPrepParseException("The input does not conform to the STD 3 ASCII rules",
198 StringPrepParseException.STD3_ASCII_RULES_ERROR,
199 processOut.toString(),
200 (poLen>0) ? poLen-1 : poLen);
208 // step 5 : verify the sequence does not begin with ACE prefix
209 if(!startsWithPrefix(processOut)){
211 //step 6: encode the sequence with punycode
212 StringBuffer punyout = PunycodeReference.encode(processOut,caseFlags);
214 // convert all codepoints to lower case ASCII
215 StringBuffer lowerOut = toASCIILower(punyout);
217 //Step 7: prepend the ACE prefix
218 dest.append(ACE_PREFIX,0,ACE_PREFIX_LENGTH);
219 //Step 6: copy the contents in b2 into dest
220 dest.append(lowerOut);
222 throw new StringPrepParseException("The input does not start with the ACE Prefix.",
223 StringPrepParseException.ACE_PREFIX_ERROR,processOut.toString(),0);
226 if(dest.length() > MAX_LABEL_LENGTH){
227 throw new StringPrepParseException("The labels in the input are too long. Length > 64.",
228 StringPrepParseException.LABEL_TOO_LONG_ERROR,dest.toString(),0);
233 public static StringBuffer convertIDNtoASCII(UCharacterIterator iter,int options)
234 throws StringPrepParseException{
235 return convertIDNToASCII(iter.getText(), options);
237 public static StringBuffer convertIDNtoASCII(StringBuffer str,int options)
238 throws StringPrepParseException{
239 return convertIDNToASCII(str.toString(), options);
241 public static StringBuffer convertIDNToASCII(String src,int options)
242 throws StringPrepParseException{
243 char[] srcArr = src.toCharArray();
244 StringBuffer result = new StringBuffer();
248 sepIndex = getSeparatorIndex(srcArr,sepIndex,srcArr.length);
249 String label = new String(srcArr,oldSepIndex,sepIndex-oldSepIndex);
250 //make sure this is not a root label separator.
251 if(!(label.length()==0 && sepIndex==srcArr.length)){
252 UCharacterIterator iter = UCharacterIterator.getInstance(label);
253 result.append(convertToASCII(iter,options));
255 if(sepIndex==srcArr.length){
258 // increment the sepIndex to skip past the separator
260 oldSepIndex = sepIndex;
261 result.append((char)FULL_STOP);
266 public static StringBuffer convertToUnicode(String src, int options)
267 throws StringPrepParseException{
268 UCharacterIterator iter = UCharacterIterator.getInstance(src);
269 return convertToUnicode(iter,options);
271 public static StringBuffer convertToUnicode(StringBuffer src, int options)
272 throws StringPrepParseException{
273 UCharacterIterator iter = UCharacterIterator.getInstance(src);
274 return convertToUnicode(iter,options);
276 public static StringBuffer convertToUnicode(UCharacterIterator iter, int options)
277 throws StringPrepParseException{
279 // the source contains all ascii codepoints
280 boolean srcIsASCII = true;
283 int saveIndex = iter.getIndex();
284 // step 1: find out if all the codepoints in src are ASCII
285 while ((ch = iter.next()) != UCharacterIterator.DONE) {
292 // The RFC states that
294 // ToUnicode never fails. If any step fails, then the original input
295 // is returned immediately in that step.
298 StringBuffer processOut;
299 if (srcIsASCII == false) {
300 // step 2: process the string
301 iter.setIndex(saveIndex);
303 processOut = transform.prepare(iter, options);
304 } catch (StringPrepParseException e) {
308 // just point to source
309 processOut = new StringBuffer(iter.getText());
312 // step 3: verify ACE Prefix
313 if (startsWithPrefix(processOut)) {
315 // step 4: Remove the ACE Prefix
316 String temp = processOut.substring(ACE_PREFIX_LENGTH, processOut.length());
318 // step 5: Decode using punycode
319 StringBuffer decodeOut = null;
321 decodeOut = PunycodeReference.decode(new StringBuffer(temp), null);
322 } catch (StringPrepParseException e) {
326 // step 6:Apply toASCII
327 StringBuffer toASCIIOut = convertToASCII(decodeOut, options);
330 if (compareCaseInsensitiveASCII(processOut, toASCIIOut) != 0) {
333 // step 8: return output of step 5
338 return new StringBuffer(iter.getText());
341 public static StringBuffer convertIDNToUnicode(UCharacterIterator iter, int options)
342 throws StringPrepParseException{
343 return convertIDNToUnicode(iter.getText(), options);
345 public static StringBuffer convertIDNToUnicode(StringBuffer str, int options)
346 throws StringPrepParseException{
347 return convertIDNToUnicode(str.toString(), options);
349 public static StringBuffer convertIDNToUnicode(String src, int options)
350 throws StringPrepParseException{
352 char[] srcArr = src.toCharArray();
353 StringBuffer result = new StringBuffer();
357 sepIndex = getSeparatorIndex(srcArr,sepIndex,srcArr.length);
358 String label = new String(srcArr,oldSepIndex,sepIndex-oldSepIndex);
359 if(label.length()==0 && sepIndex!=srcArr.length ){
360 throw new StringPrepParseException("Found zero length lable after NamePrep.",StringPrepParseException.ZERO_LENGTH_LABEL);
362 UCharacterIterator iter = UCharacterIterator.getInstance(label);
363 result.append(convertToUnicode(iter,options));
364 if(sepIndex==srcArr.length){
367 // increment the sepIndex to skip past the separator
369 oldSepIndex = sepIndex;
370 result.append((char)FULL_STOP);
375 public static int compare(StringBuffer s1, StringBuffer s2, int options)
376 throws StringPrepParseException{
377 if(s1==null || s2 == null){
378 throw new IllegalArgumentException("One of the source buffers is null");
380 StringBuffer s1Out = convertIDNToASCII(s1.toString(), options);
381 StringBuffer s2Out = convertIDNToASCII(s2.toString(), options);
382 return compareCaseInsensitiveASCII(s1Out,s2Out);
385 public static int compare(String s1, String s2, int options)
386 throws StringPrepParseException{
387 if(s1==null || s2 == null){
388 throw new IllegalArgumentException("One of the source buffers is null");
390 StringBuffer s1Out = convertIDNToASCII(s1, options);
391 StringBuffer s2Out = convertIDNToASCII(s2, options);
392 return compareCaseInsensitiveASCII(s1Out,s2Out);
395 public static int compare(UCharacterIterator i1, UCharacterIterator i2, int options)
396 throws StringPrepParseException{
397 if(i1==null || i2 == null){
398 throw new IllegalArgumentException("One of the source buffers is null");
400 StringBuffer s1Out = convertIDNToASCII(i1.getText(), options);
401 StringBuffer s2Out = convertIDNToASCII(i2.getText(), options);
402 return compareCaseInsensitiveASCII(s1Out,s2Out);