1// © 2016 and later: Unicode, Inc. and others. 2// License & terms of use: http://www.unicode.org/copyright.html#License 3/* 4 ******************************************************************************* 5 * Copyright (C) 2003-2010, International Business Machines Corporation and * 6 * others. All Rights Reserved. * 7 ******************************************************************************* 8*/ 9package com.ibm.icu.dev.test.stringprep; 10 11 12import java.lang.reflect.InvocationTargetException; 13import java.lang.reflect.Method; 14 15import com.ibm.icu.impl.ICUResourceBundle; 16import com.ibm.icu.lang.UCharacter; 17import com.ibm.icu.lang.UCharacterDirection; 18import com.ibm.icu.text.StringPrepParseException; 19import com.ibm.icu.text.UCharacterIterator; 20import com.ibm.icu.text.UnicodeSet; 21 22/** 23 * @author ram 24 * 25 * To change the template for this generated type comment go to 26 * Window>Preferences>Java>Code Generation>Code and Comments 27 */ 28public class NamePrepTransform { 29 30 private static final NamePrepTransform transform = new NamePrepTransform(); 31 32 private UnicodeSet labelSeparatorSet; 33 private UnicodeSet prohibitedSet; 34 private UnicodeSet unassignedSet; 35 private MapTransform mapTransform; 36 public static final int NONE = 0; 37 public static final int ALLOW_UNASSIGNED = 1; 38 39 private NamePrepTransform(){ 40 // load the resource bundle 41 ICUResourceBundle bundle = (ICUResourceBundle)ICUResourceBundle.getBundleInstance("com/ibm/icu/dev/data/testdata","idna_rules", NamePrepTransform.class.getClassLoader(), true); 42 String mapRules = bundle.getString("MapNoNormalization"); 43 mapRules += bundle.getString("MapNFKC"); 44 // disable 45 mapTransform = new MapTransform("CaseMap", mapRules, 0 /*Transliterator.FORWARD*/); 46 labelSeparatorSet = new UnicodeSet(bundle.getString("LabelSeparatorSet")); 47 prohibitedSet = new UnicodeSet(bundle.getString("ProhibitedSet")); 48 unassignedSet = new UnicodeSet(bundle.getString("UnassignedSet")); 49 } 50 51 public static final NamePrepTransform getInstance(){ 52 return transform; 53 } 54 public static boolean isLabelSeparator(int ch){ 55 return transform.labelSeparatorSet.contains(ch); 56 } 57 58 /* 59 1) Map -- For each character in the input, check if it has a mapping 60 and, if so, replace it with its mapping. 61 62 2) Normalize -- Possibly normalize the result of step 1 using Unicode 63 normalization. 64 65 3) Prohibit -- Check for any characters that are not allowed in the 66 output. If any are found, return an error. 67 68 4) Check bidi -- Possibly check for right-to-left characters, and if 69 any are found, make sure that the whole string satisfies the 70 requirements for bidirectional strings. If the string does not 71 satisfy the requirements for bidirectional strings, return an 72 error. 73 [Unicode3.2] defines several bidirectional categories; each character 74 has one bidirectional category assigned to it. For the purposes of 75 the requirements below, an "RandALCat character" is a character that 76 has Unicode bidirectional categories "R" or "AL"; an "LCat character" 77 is a character that has Unicode bidirectional category "L". Note 78 79 80 that there are many characters which fall in neither of the above 81 definitions; Latin digits (<U+0030> through <U+0039>) are examples of 82 this because they have bidirectional category "EN". 83 84 In any profile that specifies bidirectional character handling, all 85 three of the following requirements MUST be met: 86 87 1) The characters in section 5.8 MUST be prohibited. 88 89 2) If a string contains any RandALCat character, the string MUST NOT 90 contain any LCat character. 91 92 3) If a string contains any RandALCat character, a RandALCat 93 character MUST be the first character of the string, and a 94 RandALCat character MUST be the last character of the string. 95 */ 96 97 public boolean isReady() { 98 return mapTransform.isReady(); 99 } 100 101 public StringBuffer prepare(UCharacterIterator src, 102 int options) 103 throws StringPrepParseException{ 104 return prepare(src.getText(),options); 105 } 106 107 private String map ( String src, int options) 108 throws StringPrepParseException{ 109 // map 110 boolean allowUnassigned = ((options & ALLOW_UNASSIGNED)>0); 111 // disable test 112 String caseMapOut = mapTransform.transliterate(src); 113 UCharacterIterator iter = UCharacterIterator.getInstance(caseMapOut); 114 int ch; 115 while((ch=iter.nextCodePoint())!=UCharacterIterator.DONE){ 116 if(transform.unassignedSet.contains(ch)==true && allowUnassigned ==false){ 117 throw new StringPrepParseException("An unassigned code point was found in the input", 118 StringPrepParseException.UNASSIGNED_ERROR); 119 } 120 } 121 return caseMapOut; 122 } 123 public StringBuffer prepare(String src,int options) 124 throws StringPrepParseException{ 125 126 int ch; 127 String mapOut = map(src,options); 128 UCharacterIterator iter = UCharacterIterator.getInstance(mapOut); 129 130 int direction=UCharacterDirection.CHAR_DIRECTION_COUNT, 131 firstCharDir=UCharacterDirection.CHAR_DIRECTION_COUNT; 132 int rtlPos=-1, ltrPos=-1; 133 boolean rightToLeft=false, leftToRight=false; 134 135 while((ch=iter.nextCodePoint())!= UCharacterIterator.DONE){ 136 137 138 if(transform.prohibitedSet.contains(ch)==true && ch!=0x0020){ 139 throw new StringPrepParseException("A prohibited code point was found in the input", 140 StringPrepParseException.PROHIBITED_ERROR, 141 iter.getText(),iter.getIndex()); 142 } 143 144 direction = UCharacter.getDirection(ch); 145 if(firstCharDir == UCharacterDirection.CHAR_DIRECTION_COUNT){ 146 firstCharDir = direction; 147 } 148 if(direction == UCharacterDirection.LEFT_TO_RIGHT){ 149 leftToRight = true; 150 ltrPos = iter.getIndex()-1; 151 } 152 if(direction == UCharacterDirection.RIGHT_TO_LEFT || direction == UCharacterDirection.RIGHT_TO_LEFT_ARABIC){ 153 rightToLeft = true; 154 rtlPos = iter.getIndex()-1; 155 } 156 } 157 158 // satisfy 2 159 if( leftToRight == true && rightToLeft == true){ 160 throw new StringPrepParseException("The input does not conform to the rules for BiDi code points.", 161 StringPrepParseException.CHECK_BIDI_ERROR,iter.getText(),(rtlPos>ltrPos) ? rtlPos : ltrPos); 162 } 163 164 //satisfy 3 165 if( rightToLeft == true && 166 !((firstCharDir == UCharacterDirection.RIGHT_TO_LEFT || firstCharDir == UCharacterDirection.RIGHT_TO_LEFT_ARABIC) && 167 (direction == UCharacterDirection.RIGHT_TO_LEFT || direction == UCharacterDirection.RIGHT_TO_LEFT_ARABIC)) 168 ){ 169 throw new StringPrepParseException("The input does not conform to the rules for BiDi code points.", 170 StringPrepParseException.CHECK_BIDI_ERROR,iter.getText(),(rtlPos>ltrPos) ? rtlPos : ltrPos); 171 } 172 173 return new StringBuffer(mapOut); 174 175 } 176 177 private static class MapTransform { 178 private Object translitInstance; 179 private Method translitMethod; 180 private boolean isReady; 181 182 MapTransform(String id, String rule, int direction) { 183 isReady = initialize(id, rule, direction); 184 } 185 186 boolean initialize(String id, String rule, int direction) { 187 try { 188 Class cls = Class.forName("com.ibm.icu.text.Transliterator"); 189 Method createMethod = cls.getMethod("createFromRules", String.class, String.class, Integer.TYPE); 190 translitInstance = createMethod.invoke(null, id, rule, Integer.valueOf(direction)); 191 translitMethod = cls.getMethod("transliterate", String.class); 192 } catch (Throwable e) { 193 return false; 194 } 195 return true; 196 } 197 198 boolean isReady() { 199 return isReady; 200 } 201 202 String transliterate(String text) { 203 if (!isReady) { 204 throw new IllegalStateException("Transliterator is not ready"); 205 } 206 String result = null; 207 try { 208 result = (String)translitMethod.invoke(translitInstance, text); 209 } catch (InvocationTargetException ite) { 210 throw new RuntimeException(ite); 211 } catch (IllegalAccessException iae) { 212 throw new RuntimeException(iae); 213 } 214 return result; 215 } 216 } 217} 218