View Javadoc
1 package xdoclet.sdk.xgg.binding; 2 3 import java.util.HashMap; 4 import java.util.Map; 5 6 import xdoclet.XDocletException; 7 8 /*** 9 * Utility class that converts from xml type names to java names. This 10 * class is largely inspired from Zeus. 11 * 12 * @author <a href="mailto:aslak.hellesoy at bekk.no">Aslak Hellesøy</a> 13 * @version $Revision: 1.4 $ 14 */ 15 public class NamingUtils { 16 /*** A map of illegal characters mapped to legal alternative representations. */ 17 private static Map illegalCharMap = new HashMap(); 18 19 /*** A map of all the Java reserved words. */ 20 private static HashMap reservedWords = new HashMap(); 21 22 /*** An array of all the Java reserved words to be stored in a map. */ 23 private static String reservedWordsArray[] = {"abstract", 24 "boolean", "break", "byte", "case", "catch", "char", "class", 25 "char", "const", "continue", "default", "do", "double", "else", 26 "extends", "false", "final", "finally", "float", "for", 27 "goto", "if", "implements", "import", "instanceof", "int", 28 "interface", "long", "native", "new", "null", "package", 29 "private", "protected", "public", "return", "short", "static", 30 "super", "switch", "synchronized", "this", "throw", "throws", 31 "transient", "true", "try", "void", "volatile", "while", 32 33 // This is a quick hack to get around name clashes with xjavadoc.XTag 34 // It should be removed, and XTag.getName() and XTag.getValue() should 35 // be renamed to getTagName() and getTagValue() to minimise the risk 36 // of name clashes. 37 "name", "value" 38 }; 39 40 /*** The default alternative representation for illegal characters. */ 41 public final static String DEFAULT_MAPPING = ""; 42 43 /*** Java Name for PCDATA variable */ 44 public final static String PCDATA_JAVA_NAME = "value"; 45 /*** XML Name for PCDATA elements */ 46 public final static String PCDATA_XML_NAME = "PCDATA"; 47 48 /*** Prefix to append to names that clash with Java reserved words. */ 49 public final static String XML_PREFIX = "xml"; 50 51 52 /*** 53 * <p> 54 * Prevent direct instantiation. 55 * </p> 56 */ 57 private NamingUtils() { } 58 59 60 /*** 61 * <p> 62 * This will take a <code>String</code> with unknown capitalization, 63 * and convert all the letters to lowercase letters. 64 * Note that if you want just the first 65 * letter converted to lowercase, then 66 * <code>{@link #initialLower(String)}</code> should be used. 67 * </p><p> 68 * And for all you geniuses out there, yes, I know that 69 * <code>toLowerCase()</code> can be used directly; but 70 * placing the code within this method allows this class 71 * to be uniformly used for all capitalization purposes. 72 * </p> 73 * 74 * @param original <code>String</code> to convert. 75 * @return <code>String</code> - the converted <code>String</code>. 76 */ 77 public static String allLower(String original) { 78 if (original == null) { 79 throw new IllegalArgumentException("A non-null String must be " + 80 "supplied to CapitalizationUtils methods."); 81 } 82 83 return original.toLowerCase(); 84 } 85 86 87 /*** 88 * <p> 89 * This will take a <code>String</code> with unknown capitalization, 90 * and convert all the letters to uppercase letters. 91 * Note that if you want just the first 92 * letter converted to lowercase, then 93 * <code>{@link #initialUpper(String)}</code> should be used. 94 * </p><p> 95 * And for all you geniuses out there, yes, I know that 96 * <code>toUpperCase()</code> can be used directly; but 97 * placing the code within this method allows this class 98 * to be uniformly used for all capitalization purposes. 99 * </p> 100 * 101 * @param original <code>String</code> to convert. 102 * @return <code>String</code> - the converted <code>String</code>. 103 */ 104 public static String allUpper(String original) { 105 if (original == null) { 106 throw new IllegalArgumentException("A non-null String must be " + 107 "supplied to CapitalizationUtils methods."); 108 } 109 110 return original.toUpperCase(); 111 } 112 113 114 /*** 115 * <p> 116 * Returns the value that the given key is mapped to. This value will 117 * replace any occurence of the key when a name contains illegal 118 * characters for a Java identifier. 119 * </p> 120 * 121 * @param key the key mapped to the value to return. 122 * @return <code>String</code> - the value the given key is mapped to. 123 */ 124 public static String getCharMapping(Character key) { 125 if (key == null) { 126 throw new IllegalArgumentException("A non-null Character must be " + 127 "supplied to NamingUtils methods."); 128 } 129 130 return (String) illegalCharMap.get(key); 131 } 132 133 134 /*** 135 * <p> 136 * Returns the conventional Java class name from an XML name. The initial 137 * character is capitalized, and any illegal characters are replaced. 138 * There is no check for a collision with a Java reserved word because 139 * all Java reserved words have their initial character lower case. 140 * </p><p> 141 * For example, calling <code>getJavaClassName("my-element")</code> would 142 * return "MyElement" as the Java class name. 143 * </p> 144 * 145 * @param xmlName the XML name to convert to a Java class name. 146 * @return <code>String</code> - the converted Java name. 147 */ 148 public static String getJavaClassName(String xmlName) { 149 if (xmlName == null) { 150 throw new IllegalArgumentException("A non-null XML name must be " + 151 "supplied to NamingUtils methods."); 152 } 153 154 String result = initialUpper( 155 convertIllegalChars(convertReservedWord(xmlName))); 156 157 return result; 158 } 159 160 161 /*** 162 * <p> 163 * Returns the Java variable name from a Java class name assuming that the 164 * variable will be a collection based variable (i.e. ending with "List" 165 * as in "myVariableList"). The initial character is uncapitalized, and 166 * any illegal characters are replaced. There is no check for a 167 * collision with a Java reserved word because the assumption is that 168 * the variable has some word appended to it that identifies the fact 169 * that the variable denotes a collection. 170 * </p><p> 171 * For example, calling 172 * <code>getJavaCollectionVariableName("MyClass")</code> would return 173 * "myClass" as the Java variable name. 174 * </p> 175 * 176 * @param className the class name to convert to a collection based 177 * variable name. 178 * @return <code>String</code> - the converted Java variable name. 179 */ 180 public static String getJavaCollectionVariableName(String className) { 181 if (className == null) { 182 throw new IllegalArgumentException("A non-null class name must " + 183 "be supplied to NamingUtils methods."); 184 } 185 186 return convertReservedWord( 187 convertIllegalChars(initialLower(className))); 188 } 189 190 191 /*** 192 * <p> 193 * Returns a conventional Java name from an XML name. Any illegal 194 * characters are replaced. Collision with reserved words is <i>not</i> 195 * checked, as this is not used directly in class generation. 196 * </p><p> 197 * For example, calling <code>getJavaName("my-element")</code> would 198 * return "myElement" as the Java class name. 199 * </p> 200 * 201 * @param xmlName the XML name to convert to a Java name. 202 * @return <code>String</code> - the converted Java name. 203 */ 204 public static String getJavaName(String xmlName) { 205 if (xmlName == null) { 206 throw new IllegalArgumentException("A non-null XML name must be " + 207 "supplied to NamingUtils methods."); 208 } 209 210 // Handle the PCDATA special case 211 if (xmlName.equals(PCDATA_XML_NAME)) { 212 return convertIllegalChars(PCDATA_JAVA_NAME); 213 } 214 215 return convertReservedWord(convertIllegalChars(xmlName)); 216 } 217 218 219 /*** 220 * <p> 221 * This will handle conversion from an XML type to a Java type. It first 222 * attempts to perform an XML Schema type to Java type conversion. For 223 * example, if the XML type is "string", it would convert this to 224 * the Java type "String". If that is unsuccessful, it returns the 225 * type unchanged, assuming that it is a proper Java class name. 226 * </p> 227 * 228 * @param xmlType the XML type to convert to a Java type. 229 * @return <code>String</code> - the converted Java type. 230 */ 231 public static String getJavaType(String xmlType) { 232 String javaType; 233 try { 234 javaType = getSchemaJavaType(xmlType); 235 return javaType; 236 } catch (XDocletException e) { 237 e.printStackTrace(); 238 } 239 240 // Ensure a legal Java class name is returned 241 return getJavaClassName(xmlType); 242 } 243 244 245 /*** 246 * <p> 247 * Returns the conventional Java variable name from an XML name. The 248 * initial character is uncapitalized, and any illegal characters are 249 * replaced. If there is a collision with a Java reserved word, the name 250 * is prefixed with <code>{@link #XML_PREFIX}</code>. 251 * </p><p> 252 * For example, calling <code>getJavaVariableName("XmlName")</code> 253 * would return "xmlName" as the Java variable name. 254 * </p> 255 * 256 * @param xmlName the XML name to convert to a variable name. 257 * @return <code>String</code> - the converted Java variable name. 258 */ 259 public static String getJavaVariableName(String xmlName) { 260 if (xmlName == null) { 261 throw new IllegalArgumentException("A non-null XML name must " + 262 "be supplied to NamingUtils methods."); 263 } 264 265 // Handle the PCDATA special case 266 if (xmlName.equals(PCDATA_XML_NAME)) { 267 return convertReservedWord( 268 initialLower(PCDATA_JAVA_NAME)); 269 } 270 271 return convertReservedWord( 272 convertIllegalChars(initialLower(xmlName))); 273 274 } 275 276 277 /*** 278 * <p> 279 * This will take as input an XML Schema data type (such as "string") 280 * and convert it to a Java data type (such as "String"). It includes, 281 * if needed, the fully-qualified package (such as "java.util.List") to 282 * prevent import problems. 283 * </p> 284 * <p><b>Note:</b> Eventually this should probably work in concert with 285 * some methodology that handles package imports and the like.</p> 286 * 287 * @param schemaType the XML Schema data type (as a <code>String</code>) 288 * @return <code>String</code> - the Java data type. 289 * @throws XDocletException - when the supplied 290 * schema type is not supported. 291 */ 292 public static String getSchemaJavaType(String schemaType) 293 throws XDocletException { 294 295 if (schemaType == null) { 296 throw new IllegalArgumentException("A non-null schema type must " + 297 "be supplied to SchemaUtils methods."); 298 } 299 300 if (schemaType.equals("string")) { 301 return "java.lang.String"; 302 } 303 304 // If we got here, it's an unsupported or incorrect data type 305 throw new XDocletException(schemaType); 306 } 307 308 309 /*** 310 * <p> 311 * Returns the XML name from a typical accessor method. The naming 312 * convention for such a method is "get<variable-name>". The first 313 * three characters are discarded, and the initial character is 314 * uncapitalized. 315 * </p><p> 316 * For example, calling 317 * <code>getXMLElementNameFromAccessor("getMyVariable")</code> would 318 * return "myVariable" as the XML name. This method would also work 319 * with typical mutator methods. 320 * </p> 321 * 322 * @param accessor the name of the accessor method to derive the XML name 323 * from. 324 * @return <code>String</code> - the converted XML name. 325 */ 326 public static String getXMLElementNameFromAccessor(String accessor) { 327 if (accessor == null) { 328 throw new IllegalArgumentException("A non-null accessor must " + 329 "be supplied to NamingUtils methods."); 330 } 331 332 return initialLower(accessor.substring(3)); 333 } 334 335 336 /*** 337 * <p> 338 * This will take a <code>String</code> with unknown capitalization, 339 * and convert the first letter to a lowercase letter, while leaving 340 * all other letters the same. Note that if you want the rest of 341 * the letters converted to lowercase, then 342 * <code>{@link #allLower(String)}</code> should be used. 343 * </p> 344 * 345 * @param original <code>String</code> to convert. 346 * @return <code>String</code> - the converted <code>String</code>. 347 */ 348 public static String initialLower(String original) { 349 if (original == null) { 350 throw new IllegalArgumentException("A non-null String must be " + 351 "supplied to CapitalizationUtils methods."); 352 } 353 if (original.length() == 0) { 354 return original; 355 } 356 357 char[] originalChars = original.toCharArray(); 358 char firstLetter = 359 Character.toLowerCase(original.charAt(0)); 360 originalChars[0] = firstLetter; 361 362 return new String(originalChars); 363 } 364 365 366 /*** 367 * <p> 368 * This will take a <code>String</code> with unknown capitalization, 369 * and convert the first letter to a capital letter, while leaving 370 * all other letters the same. Note that if you want the rest of 371 * the letters converted to lowercase, then 372 * <code>{@link #justInitialUpper(String)}</code> should be used. 373 * </p> 374 * 375 * @param original <code>String</code> to convert. 376 * @return <code>String</code> - the converted <code>String</code>. 377 */ 378 public static String initialUpper(String original) { 379 if (original == null) { 380 throw new IllegalArgumentException("A non-null String must be " + 381 "supplied to CapitalizationUtils methods."); 382 } 383 if (original.length() == 0) { 384 return original; 385 } 386 387 char[] originalChars = original.toCharArray(); 388 char firstLetter = 389 Character.toUpperCase(original.charAt(0)); 390 originalChars[0] = firstLetter; 391 392 return new String(originalChars); 393 } 394 395 396 /*** 397 * <p> 398 * This will indicate whether the supplied name is a legal Java class 399 * name. 400 * </p> 401 * 402 * @param className the Java class name to check 403 * @return <code>boolean</code> - whether the supplied class is a legal 404 * Java class name. 405 */ 406 public static boolean isLegalJavaClassName(String className) { 407 if (className == null) { 408 throw new IllegalArgumentException("A non-null String must be " + 409 "supplied to NamingUtils methods."); 410 } 411 412 for (int i = 0, len = className.length(); i < len; i++) { 413 if ((i == 0) && 414 (!Character.isJavaIdentifierStart(className.charAt(i)))) { 415 return false; 416 } else if (!Character.isJavaIdentifierPart(className.charAt(i))) { 417 return false; 418 } 419 } 420 421 // If we got here, all is OK 422 return true; 423 } 424 425 426 /*** 427 * <p> 428 * This will indicate whether the supplied name is a legal Java package 429 * name. 430 * </p> 431 * 432 * @param packageName the Java class name to check 433 * @return <code>boolean</code> - whether the supplied package is a legal 434 * Java package name. 435 */ 436 public static boolean isLegalJavaPackageName(String packageName) { 437 <b>if (packageName == null) { 438 throw new IllegalArgumentException("A non-null String must be " + 439 "supplied to NamingUtils methods."); 440 } 441 442 for (int i = 0, len = packageName.length(); i < len; i++) { 443 if ((i == 0) && 444 (!Character.isJavaIdentifierStart(packageName.charAt(i)))) { 445 return false; 446 } else if ( 447 (!Character.isJavaIdentifierPart(packageName.charAt(i))) && 448 (!(packageName.charAt(i) == '.'))) { 449 450 return false; 451 } 452 } 453 454 // If we got here, all is OK 455 return true; 456 } 457 458 459 /*** 460 * <p> 461 * This will take a <code>String</code> with unknown capitalization, 462 * and convert the first letter to a capital letter, while converting 463 * all other letters to lowercase. Note that if you want the rest of 464 * the letters left alone, then 465 * <code>{@link #initialUpper(String)}</code> should be used. 466 * </p> 467 * 468 * @param original <code>String</code> to convert. 469 * @return <code>String</code> - the converted <code>String</code>. 470 */ 471 public static String justInitialUpper(String original) { 472 if (original == null) { 473 throw new IllegalArgumentException("A non-null String must be " + 474 "supplied to CapitalizationUtils methods."); 475 } 476 477 String lowercaseOriginal = allLower(original); 478 return initialUpper(lowercaseOriginal); 479 } 480 481 482 /*** 483 * <p> 484 * This will take a fully qualified Java class, and return just the name 485 * of the class, without any package qualifier. For example, invoking 486 * <code>removePackage("java.util.List");</code> would return 487 * "List". 488 * </p> 489 * 490 * @param className the Java class name to remove the package from. 491 * @return <code>String</code> - the Java class, without package. 492 */ 493 public static String removePackage(String className) { 494 String returnValue = className; 495 496 if (returnValue.indexOf(".") > -1) { 497 returnValue = returnValue.substring( 498 returnValue.lastIndexOf(".") + 1); 499 } 500 501 return returnValue; 502 } 503 504 505 /*** 506 * <p> 507 * Maps the given key to the given value. This value will replace any 508 * occurence of the key when a name contains illegal characters 509 * for a Java identifier. 510 * </p> 511 * 512 * @param key the key to map the given value to. 513 * @param value the value to map the given key to. 514 */ 515 public static void setCharMapping(Character key, String value) { 516 if (key == null) { 517 throw new IllegalArgumentException("A non-null char key must be " + 518 "supplied to NamingUtils methods."); 519 } 520 if (value == null) { 521 throw new IllegalArgumentException("A non-null value must be " + 522 "supplied to NamingUtils methods."); 523 } 524 525 illegalCharMap.put(key, value); 526 } 527 528 529 /*** 530 * <p> 531 * Checks whether each character in the given name is valid for a Java 532 * identifier. If there are illegal characters, the name is returned 533 * with each illegal character replaced with its value in the internal 534 * map of illegal <code>Character</code>s to replacement 535 * <code>String</code>s, else the same name is returned. If the 536 * illegal character can be interpreted as a separator character 537 * (such as the '-' in my-attribute), then the character following the 538 * separator character will be capitalized (i.e. my-attribute becomes 539 * myAttribute). 540 * </p> 541 * 542 * @param name the name to check. 543 * @return <code>String</code> - the converted name if illegal characters 544 * are present, else the same name. 545 */ 546 private static String convertIllegalChars(String name) { 547 if (name == null) { 548 throw new IllegalArgumentException("A non-null name must " + 549 "be supplied to NamingUtils methods."); 550 } 551 552 StringBuffer verifiedName = new StringBuffer(); 553 char[] characters = name.toCharArray(); 554 555 // XXX: Uppercase the next letter after all illegal chars, or just the 556 // ones that can be interpreted as separator chars? 557 boolean upperNext = false; 558 559 for (int i = 0; i < characters.length; i++) { 560 char c = characters[i]; 561 562 if (i == 0) { 563 if (Character.isJavaIdentifierStart(c) == false) { 564 String legalValue = 565 (String) illegalCharMap.get(new Character(c)); 566 567 if (legalValue == null) { 568 569 // XXX: Throw an exception or append DEFAULT_MAPPING? 570 throw new IllegalArgumentException("No mapping for " + 571 "illegal character: " + c); 572 } else { 573 verifiedName.append(legalValue); 574 575 // XXX Does it make sense to uppercase the next char 576 // if the illegal char is the first char of the 577 // identifier? 578 upperNext = true; 579 } 580 } else { 581 verifiedName.append(c); 582 } 583 } else { 584 if (Character.isJavaIdentifierPart(c) == false) { 585 String legalValue = 586 (String) illegalCharMap.get(new Character(c)); 587 588 if (legalValue == null) { 589 590 // XXX: Throw an exception or append DEFAULT_MAPPING? 591 throw new IllegalArgumentException("No mapping for " + 592 "illegal character: " + c); 593 } else { 594 verifiedName.append(legalValue); 595 upperNext = true; 596 } 597 } else { 598 if (upperNext) { 599 verifiedName.append(Character.toUpperCase(c)); 600 upperNext = false; 601 } else { 602 verifiedName.append(c); 603 } 604 } 605 } 606 } 607 return verifiedName.toString(); 608 } 609 610 611 /*** 612 * <p> 613 * Checks whether the given name collides with a Java reserved word. If 614 * there is a collision, the name is returned prefixed with 615 * <code>{@link #XML_PREFIX}</code>, else it is returned unchanged. 616 * </p> 617 * 618 * @param name the name to check. 619 * @return <code>String</code> - the converted name if there is a collision 620 * with a Java reserved word, else the same name. 621 */ 622 private static String convertReservedWord(String name) { 623 if (name == null) { 624 throw new IllegalArgumentException("A non-null name must " + 625 "be supplied to NamingUtils methods."); 626 } 627 628 if (reservedWords.get(name) == null) { 629 return name; 630 } else { 631 return new StringBuffer().append(XML_PREFIX) 632 .append(name).toString(); 633 } 634 } 635 636 // Initialize the reserved words map and the illegal characters map. 637 static { 638 for (int i = 0; i < reservedWordsArray.length; i++) { 639 reservedWords.put(reservedWordsArray[i], new Integer(i)); 640 } 641 reservedWordsArray = null; 642 643 // XXX: Add more illegal chars mappings...we don't have to check too 644 // many because we can assume XML naming rules, though 645 illegalCharMap.put(new Character('.'), DEFAULT_MAPPING); 646 illegalCharMap.put(new Character('-'), DEFAULT_MAPPING); 647 illegalCharMap.put(new Character(':'), DEFAULT_MAPPING); 648 } 649 }

This page was automatically generated by Maven