1 /*
   2  * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package com.sun.corba.se.impl.presentation.rmi ;
  27 
  28 import java.security.AccessController;
  29 import java.security.PrivilegedAction;
  30 
  31 import java.lang.reflect.Method;
  32 
  33 import java.math.BigInteger;
  34 
  35 import java.util.Map;
  36 import java.util.Set;
  37 import java.util.HashSet;
  38 import java.util.Iterator;
  39 import java.util.HashMap;
  40 import java.util.StringTokenizer;
  41 
  42 import com.sun.corba.se.spi.presentation.rmi.IDLNameTranslator ;
  43 
  44 import com.sun.corba.se.impl.presentation.rmi.IDLType ;
  45 import com.sun.corba.se.impl.presentation.rmi.IDLTypeException ;
  46 import com.sun.corba.se.impl.presentation.rmi.IDLTypesUtil ;
  47 import com.sun.corba.se.impl.orbutil.ObjectUtility ;
  48 
  49 /**
  50  * Bidirectional translator between RMI-IIOP interface methods and
  51  * and IDL Names.
  52  */
  53 public class IDLNameTranslatorImpl implements IDLNameTranslator {
  54 
  55     // From CORBA Spec, Table 6 Keywords.
  56     // Note that since all IDL identifiers are case
  57     // insensitive, java identifier comparisons to these
  58     // will be case insensitive also.
  59     private static String[] IDL_KEYWORDS = {
  60 
  61         "abstract", "any", "attribute", "boolean", "case", "char",
  62         "const", "context", "custom", "default", "double", "enum",
  63         "exception", "factory", "FALSE", "fixed", "float", "in", "inout",
  64         "interface", "long", "module", "native", "Object", "octet",
  65         "oneway", "out", "private", "public", "raises", "readonly", "sequence",
  66         "short", "string", "struct", "supports", "switch", "TRUE", "truncatable",
  67         "typedef", "unsigned", "union", "ValueBase", "valuetype", "void",
  68         "wchar", "wstring"
  69 
  70     };
  71 
  72     private static char[] HEX_DIGITS = {
  73         '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
  74         'A', 'B', 'C', 'D', 'E', 'F'
  75     };
  76 
  77     private static final String UNDERSCORE = "_";
  78 
  79     // used to mangle java inner class names
  80     private static final String INNER_CLASS_SEPARATOR =
  81         UNDERSCORE + UNDERSCORE;
  82 
  83     // used to form IDL array type names
  84     private static final String[] BASE_IDL_ARRAY_MODULE_TYPE=
  85         new String[] { "org", "omg", "boxedRMI" } ;
  86 
  87     private static final String BASE_IDL_ARRAY_ELEMENT_TYPE = "seq";
  88 
  89     // used to mangling java identifiers that have a leading underscore
  90     private static final String LEADING_UNDERSCORE_CHAR = "J";
  91     private static final String ID_CONTAINER_CLASH_CHAR = UNDERSCORE;
  92 
  93     // separator used between types in a mangled overloaded method name
  94     private static final String OVERLOADED_TYPE_SEPARATOR =
  95         UNDERSCORE + UNDERSCORE;
  96 
  97     // string appended to attribute if it clashes with a method name
  98     private static final String ATTRIBUTE_METHOD_CLASH_MANGLE_CHARS =
  99         UNDERSCORE + UNDERSCORE;
 100 
 101     // strings prepended to the attribute names in order to form their
 102     // IDL names.
 103     private static final String GET_ATTRIBUTE_PREFIX = "_get_";
 104     private static final String SET_ATTRIBUTE_PREFIX = "_set_";
 105     private static final String IS_ATTRIBUTE_PREFIX  = "_get_";
 106 
 107     private static Set idlKeywords_;
 108 
 109     static {
 110 
 111         idlKeywords_ = new HashSet();
 112         for(int i = 0; i < IDL_KEYWORDS.length; i++) {
 113             String next = (String) IDL_KEYWORDS[i];
 114             // Convert keyword to all caps to ease equality
 115             // check.
 116             String keywordAllCaps = next.toUpperCase();
 117             idlKeywords_.add(keywordAllCaps);
 118         }
 119 
 120     }
 121 
 122     //
 123     // Instance state
 124     //
 125 
 126     // Remote interface for name translation.
 127     private Class[] interf_;
 128 
 129     // Maps used to hold name translations.  These do not need to be
 130     // synchronized since the translation is never modified after
 131     // initialization.
 132     private Map methodToIDLNameMap_;
 133     private Map IDLNameToMethodMap_;
 134     private Method[] methods_;
 135 
 136     /**
 137      * Return an IDLNameTranslator for the given interface.
 138      *
 139      * @throws IllegalStateException if given class is not a valid
 140      *         RMI/IIOP Remote Interface
 141      */
 142     public static IDLNameTranslator get( Class interf )
 143     {
 144 
 145         return new IDLNameTranslatorImpl(new Class[] { interf } );
 146 
 147     }
 148 
 149     /**
 150      * Return an IDLNameTranslator for the given interfacex.
 151      *
 152      * @throws IllegalStateException if given classes are not  valid
 153      *         RMI/IIOP Remote Interfaces
 154      */
 155     public static IDLNameTranslator get( Class[] interfaces )
 156     {
 157 
 158         return new IDLNameTranslatorImpl(interfaces );
 159 
 160     }
 161 
 162     public static String getExceptionId( Class cls )
 163     {
 164         // Requirements for this method:
 165         // 1. cls must be an exception but not a RemoteException.
 166         // 2. If cls has an IDL keyword name, an underscore is prepended (1.3.2.2).
 167         // 3. If cls jas a leading underscore, J is prepended (1.3.2.3).
 168         // 4. If cls has an illegal IDL ident char, it is mapped to UXXXX where
 169         //    XXXX is the unicode value in hex of the char (1.3.2.4).
 170         // 5. double underscore for inner class (1.3.2.5).
 171         // 6. The ID is "IDL:" + name with / separators + ":1.0".
 172         IDLType itype = classToIDLType( cls ) ;
 173         return itype.getExceptionName() ;
 174     }
 175 
 176     public Class[] getInterfaces()
 177     {
 178         return interf_;
 179     }
 180 
 181     public Method[] getMethods()
 182     {
 183         return methods_ ;
 184     }
 185 
 186     public Method getMethod( String idlName )
 187     {
 188         return (Method) IDLNameToMethodMap_.get(idlName);
 189     }
 190 
 191     public String getIDLName( Method method )
 192     {
 193         return (String) methodToIDLNameMap_.get(method);
 194     }
 195 
 196     /**
 197      * Initialize an IDLNameTranslator for the given interface.
 198      *
 199      * @throws IllegalStateException if given class is not a valid
 200      *         RMI/IIOP Remote Interface
 201      */
 202     private IDLNameTranslatorImpl(Class[] interfaces)
 203     {
 204 
 205         SecurityManager s = System.getSecurityManager();
 206         if (s != null) {
 207             s.checkPermission(new DynamicAccessPermission("access"));
 208         }
 209         try {
 210             IDLTypesUtil idlTypesUtil = new IDLTypesUtil();
 211             for (int ctr=0; ctr<interfaces.length; ctr++)
 212                 idlTypesUtil.validateRemoteInterface(interfaces[ctr]);
 213             interf_ = interfaces;
 214             buildNameTranslation();
 215         } catch( IDLTypeException ite) {
 216             String msg = ite.getMessage();
 217             IllegalStateException ise = new IllegalStateException(msg);
 218             ise.initCause(ite);
 219             throw ise;
 220         }
 221     }
 222 
 223     private void buildNameTranslation()
 224     {
 225         // holds method info, keyed by method
 226         Map allMethodInfo = new HashMap() ;
 227 
 228         for (int ctr=0; ctr<interf_.length; ctr++) {
 229             Class interf = interf_[ctr] ;
 230 
 231             IDLTypesUtil idlTypesUtil = new IDLTypesUtil();
 232             final Method[] methods = interf.getMethods();
 233             // Handle the case of a non-public interface!
 234             AccessController.doPrivileged(new PrivilegedAction() {
 235                 public Object run() {
 236                     Method.setAccessible( methods, true ) ;
 237                     return null ;
 238                 }
 239             } ) ;
 240 
 241             // Take an initial pass through all the methods and create some
 242             // information that will be used to track the IDL name
 243             // transformation.
 244             for(int i = 0; i < methods.length; i++) {
 245 
 246                 Method nextMethod = methods[i];
 247 
 248                 IDLMethodInfo methodInfo = new IDLMethodInfo();
 249 
 250                 methodInfo.method = nextMethod;
 251 
 252                 if (idlTypesUtil.isPropertyAccessorMethod(nextMethod, interf)) {
 253                     methodInfo.isProperty = true;
 254                     String attributeName = idlTypesUtil.
 255                         getAttributeNameForProperty(nextMethod.getName());
 256                     methodInfo.originalName = attributeName;
 257                     methodInfo.mangledName  = attributeName;
 258                 } else {
 259                     methodInfo.isProperty = false;
 260                     methodInfo.originalName = nextMethod.getName();
 261                     methodInfo.mangledName  = nextMethod.getName();
 262                 }
 263 
 264                 allMethodInfo.put(nextMethod, methodInfo);
 265             }
 266         }
 267 
 268         //
 269         // Perform case sensitivity test first.  This applies to all
 270         // method names AND attributes.  Compare each method name and
 271         // attribute to all other method names and attributes.  If names
 272         // differ only in case, apply mangling as defined in section 1.3.2.7
 273         // of Java2IDL spec.  Note that we compare using the original names.
 274         //
 275         for(Iterator outerIter=allMethodInfo.values().iterator();
 276             outerIter.hasNext();) {
 277             IDLMethodInfo outer = (IDLMethodInfo) outerIter.next();
 278             for(Iterator innerIter = allMethodInfo.values().iterator();
 279                 innerIter.hasNext();) {
 280                 IDLMethodInfo inner = (IDLMethodInfo) innerIter.next();
 281 
 282                 if( (outer != inner) &&
 283                     (!outer.originalName.equals(inner.originalName)) &&
 284                     outer.originalName.equalsIgnoreCase(inner.originalName) ) {
 285                     outer.mangledName =
 286                         mangleCaseSensitiveCollision(outer.originalName);
 287                     break;
 288                 }
 289 
 290             }
 291         }
 292 
 293         for(Iterator iter = allMethodInfo.values().iterator();
 294             iter.hasNext();) {
 295             IDLMethodInfo next = (IDLMethodInfo) iter.next();
 296             next.mangledName =
 297                 mangleIdentifier(next.mangledName, next.isProperty);
 298         }
 299 
 300         //
 301         // Now check for overloaded method names and apply 1.3.2.6.
 302         //
 303         for(Iterator outerIter=allMethodInfo.values().iterator();
 304             outerIter.hasNext();) {
 305             IDLMethodInfo outer = (IDLMethodInfo) outerIter.next();
 306             if( outer.isProperty ) {
 307                 continue;
 308             }
 309             for(Iterator innerIter = allMethodInfo.values().iterator();
 310                 innerIter.hasNext();) {
 311                 IDLMethodInfo inner = (IDLMethodInfo) innerIter.next();
 312 
 313                 if( (outer != inner) &&
 314                     !inner.isProperty &&
 315                     outer.originalName.equals(inner.originalName) ) {
 316                     outer.mangledName = mangleOverloadedMethod
 317                         (outer.mangledName, outer.method);
 318                     break;
 319                 }
 320             }
 321         }
 322 
 323         //
 324         // Now mangle any properties that clash with method names.
 325         //
 326         for(Iterator outerIter=allMethodInfo.values().iterator();
 327             outerIter.hasNext();) {
 328             IDLMethodInfo outer = (IDLMethodInfo) outerIter.next();
 329             if( !outer.isProperty ) {
 330                 continue;
 331             }
 332             for(Iterator innerIter = allMethodInfo.values().iterator();
 333                 innerIter.hasNext();) {
 334                 IDLMethodInfo inner = (IDLMethodInfo) innerIter.next();
 335                 if( (outer != inner) &&
 336                     !inner.isProperty &&
 337                     outer.mangledName.equals(inner.mangledName) ) {
 338                     outer.mangledName = outer.mangledName +
 339                         ATTRIBUTE_METHOD_CLASH_MANGLE_CHARS;
 340                     break;
 341                 }
 342             }
 343         }
 344 
 345         //
 346         // Ensure that no mapped method names clash with mapped name
 347         // of container(1.3.2.9).  This is a case insensitive comparison.
 348         //
 349         for (int ctr=0; ctr<interf_.length; ctr++ ) {
 350             Class interf = interf_[ctr] ;
 351             String mappedContainerName = getMappedContainerName(interf);
 352             for(Iterator iter = allMethodInfo.values().iterator();
 353                 iter.hasNext();) {
 354                 IDLMethodInfo next = (IDLMethodInfo) iter.next();
 355                 if( !next.isProperty &&
 356                     identifierClashesWithContainer(mappedContainerName,
 357                                                    next.mangledName)) {
 358                     next.mangledName = mangleContainerClash(next.mangledName);
 359                 }
 360             }
 361         }
 362 
 363         //
 364         // Populate name translation maps.
 365         //
 366         methodToIDLNameMap_ = new HashMap();
 367         IDLNameToMethodMap_ = new HashMap();
 368         methods_ = (Method[])allMethodInfo.keySet().toArray(
 369             new Method[0] ) ;
 370 
 371         for(Iterator iter = allMethodInfo.values().iterator();
 372             iter.hasNext();) {
 373             IDLMethodInfo next = (IDLMethodInfo) iter.next();
 374             String idlName = next.mangledName;
 375             if( next.isProperty ) {
 376                 String origMethodName = next.method.getName();
 377                 String prefix = "";
 378 
 379                 if( origMethodName.startsWith("get") ) {
 380                     prefix = GET_ATTRIBUTE_PREFIX;
 381                 } else if( origMethodName.startsWith("set") ) {
 382                     prefix = SET_ATTRIBUTE_PREFIX;
 383                 } else {
 384                     prefix = IS_ATTRIBUTE_PREFIX;
 385                 }
 386 
 387                 idlName = prefix + next.mangledName;
 388             }
 389 
 390             methodToIDLNameMap_.put(next.method, idlName);
 391 
 392             // Final check to see if there are any clashes after all the
 393             // manglings have been applied.  If so, this is treated as an
 394             // invalid interface.  Currently, we do a CASE-SENSITIVE
 395             // comparison since that matches the rmic behavior.
 396             // @@@ Shouldn't this be a case-insensitive check?
 397             if( IDLNameToMethodMap_.containsKey(idlName) ) {
 398                 // @@@ I18N
 399                 Method clash = (Method) IDLNameToMethodMap_.get(idlName);
 400                 throw new IllegalStateException("Error : methods " +
 401                     clash + " and " + next.method +
 402                     " both result in IDL name '" + idlName + "'");
 403             } else {
 404                 IDLNameToMethodMap_.put(idlName, next.method);
 405             }
 406         }
 407 
 408         return;
 409 
 410     }
 411 
 412 
 413     /**
 414      * Perform all necessary stand-alone identifier mangling operations
 415      * on a java identifier that is being transformed into an IDL name.
 416      * That is, mangling operations that don't require looking at anything
 417      * else but the identifier itself.  This covers sections 1.3.2.2, 1.3.2.3,
 418      * and 1.3.2.4 of the Java2IDL spec.  Method overloading and
 419      * case-sensitivity checks are handled elsewhere.
 420      */
 421 
 422     private static String mangleIdentifier(String identifier) {
 423         return mangleIdentifier(identifier, false);
 424     }
 425 
 426     private static String mangleIdentifier(String identifier, boolean attribute) {
 427 
 428         String mangledName = identifier;
 429 
 430         //
 431         // Apply leading underscore test (1.3.2.3)
 432         // This should be done before IDL Keyword clash test, since clashing
 433         // IDL keywords are mangled by adding a leading underscore.
 434         //
 435         if( hasLeadingUnderscore(mangledName) ) {
 436             mangledName = mangleLeadingUnderscore(mangledName);
 437         }
 438 
 439         //
 440         // Apply IDL keyword clash test (1.3.2.2).
 441         // This is not needed for attributes since when the full property
 442         // name is composed it cannot clash with an IDL keyword.
 443         // (Also, rmic doesn't do it.)
 444         //
 445 
 446         if( !attribute && isIDLKeyword(mangledName) ) {
 447             mangledName = mangleIDLKeywordClash(mangledName);
 448         }
 449 
 450         //
 451         // Replace illegal IDL identifier characters (1.3.2.4)
 452         // for all method names and attributes.
 453         //
 454         if( !isIDLIdentifier(mangledName) ) {
 455             mangledName = mangleUnicodeChars(mangledName);
 456         }
 457 
 458         return mangledName;
 459     }
 460 
 461     // isIDLKeyword and mangleIDLKeywordClash are exposed here so that
 462     // IDLType can use them.
 463     //
 464     // XXX refactoring needed:
 465     // 1. Split off isIDLKeywork and mangleIDLKeywordClash (and possibly
 466     //    other methods) into a utility class.
 467     // 2. Move all of classToIDLType to a constructor inside IDLType.
 468     //
 469     // The problem appears to be that we need all of the code that
 470     // performs various checks for name problems and the corresponding
 471     // fixes into a utility class.  Then we need to see what other
 472     // refactorings present themselves.
 473 
 474     /**
 475      * Checks whether a java identifier clashes with an
 476      * IDL keyword.  Note that this is a case-insensitive
 477      * comparison.
 478      *
 479      * Used to implement section 1.3.2.2 of Java2IDL spec.
 480      */
 481     static boolean isIDLKeyword(String identifier) {
 482 
 483         String identifierAllCaps = identifier.toUpperCase();
 484 
 485         return idlKeywords_.contains(identifierAllCaps);
 486     }
 487 
 488     static String mangleIDLKeywordClash(String identifier) {
 489         return UNDERSCORE + identifier;
 490     }
 491 
 492     private static String mangleLeadingUnderscore(String identifier) {
 493         return LEADING_UNDERSCORE_CHAR + identifier;
 494     }
 495 
 496     /**
 497      * Checks whether a java identifier starts with an underscore.
 498      * Used to implement section 1.3.2.3 of Java2IDL spec.
 499      */
 500     private static boolean hasLeadingUnderscore(String identifier) {
 501         return identifier.startsWith(UNDERSCORE);
 502     }
 503 
 504     /**
 505      * Implements Section 1.3.2.4 of Java2IDL Mapping.
 506      * All non-IDL identifier characters must be replaced
 507      * with their Unicode representation.
 508      */
 509     static String mangleUnicodeChars(String identifier) {
 510         StringBuffer mangledIdentifier = new StringBuffer();
 511 
 512         for(int i = 0; i < identifier.length(); i++) {
 513             char nextChar = identifier.charAt(i);
 514             if( isIDLIdentifierChar(nextChar) ) {
 515                 mangledIdentifier.append(nextChar);
 516             } else {
 517                 String unicode = charToUnicodeRepresentation(nextChar);
 518                 mangledIdentifier.append(unicode);
 519             }
 520         }
 521 
 522         return mangledIdentifier.toString();
 523     }
 524 
 525     /**
 526      * Implements mangling portion of Section 1.3.2.7 of Java2IDL spec.
 527      * This method only deals with the actual mangling.  Decision about
 528      * whether case-sensitive collision mangling is required is made
 529      * elsewhere.
 530      *
 531      *
 532      * "...a mangled name is generated consisting of the original name
 533      * followed by an underscore separated list of decimal indices
 534      * into the string, where the indices identify all the upper case
 535      * characters in the original string. Indices are zero based."
 536      *
 537      */
 538     String mangleCaseSensitiveCollision(String identifier) {
 539 
 540         StringBuffer mangledIdentifier = new StringBuffer(identifier);
 541 
 542         // There is always at least one trailing underscore, whether or
 543         // not the identifier has uppercase letters.
 544         mangledIdentifier.append(UNDERSCORE);
 545 
 546         boolean needUnderscore = false;
 547         for(int i = 0; i < identifier.length(); i++) {
 548             char next = identifier.charAt(i);
 549             if( Character.isUpperCase(next) ) {
 550                 // This bit of logic is needed to ensure that we have
 551                 // an underscore separated list of indices but no
 552                 // trailing underscores.  Basically, after we have at least
 553                 // one uppercase letter, we always put an undercore before
 554                 // printing the next one.
 555                 if( needUnderscore ) {
 556                     mangledIdentifier.append(UNDERSCORE);
 557                 }
 558                 mangledIdentifier.append(i);
 559                 needUnderscore = true;
 560             }
 561         }
 562 
 563         return mangledIdentifier.toString();
 564     }
 565 
 566     private static String mangleContainerClash(String identifier) {
 567         return identifier + ID_CONTAINER_CLASH_CHAR;
 568     }
 569 
 570     /**
 571      * Implements Section 1.3.2.9 of Java2IDL Mapping. Container in this
 572      * context means the name of the java Class(excluding package) in which
 573      * the identifier is defined.  Comparison is case-insensitive.
 574      */
 575     private static boolean identifierClashesWithContainer
 576         (String mappedContainerName, String identifier) {
 577 
 578         return identifier.equalsIgnoreCase(mappedContainerName);
 579     }
 580 
 581     /**
 582      * Returns Unicode mangling as defined in Section 1.3.2.4 of
 583      * Java2IDL spec.
 584      *
 585      * "For Java identifiers that contain illegal OMG IDL identifier
 586      * characters such as '$' or Unicode characters outside of ISO Latin 1,
 587      * any such illegal characters are replaced by "U" followed by the
 588      * 4 hexadecimal characters(in upper case) representing the Unicode
 589      * value.  So, the Java name a$b is mapped to aU0024b and
 590      * x\u03bCy is mapped to xU03BCy."
 591      */
 592     public static String charToUnicodeRepresentation(char c) {
 593 
 594         int orig = (int) c;
 595         StringBuffer hexString = new StringBuffer();
 596 
 597         int value = orig;
 598 
 599         while( value > 0 ) {
 600             int div = value / 16;
 601             int mod = value % 16;
 602             hexString.insert(0, HEX_DIGITS[mod]);
 603             value = div;
 604         }
 605 
 606         int numZerosToAdd = 4 - hexString.length();
 607         for(int i = 0; i < numZerosToAdd; i++) {
 608             hexString.insert(0, "0");
 609         }
 610 
 611         hexString.insert(0, "U");
 612         return hexString.toString();
 613     }
 614 
 615     private static boolean isIDLIdentifier(String identifier) {
 616 
 617         boolean isIdentifier = true;
 618 
 619         for(int i = 0; i < identifier.length(); i++) {
 620             char nextChar = identifier.charAt(i);
 621             // 1st char must be alphbetic.
 622             isIdentifier  = (i == 0) ?
 623                 isIDLAlphabeticChar(nextChar) :
 624                 isIDLIdentifierChar(nextChar);
 625             if( !isIdentifier ) {
 626                 break;
 627             }
 628         }
 629 
 630         return isIdentifier;
 631 
 632     }
 633 
 634     private static boolean isIDLIdentifierChar(char c) {
 635         return (isIDLAlphabeticChar(c) ||
 636                 isIDLDecimalDigit(c)   ||
 637                 isUnderscore(c));
 638     }
 639 
 640     /**
 641      * True if character is one of 114 Alphabetic characters as
 642      * specified in Table 2 of Chapter 3 in CORBA spec.
 643      */
 644     private static boolean isIDLAlphabeticChar(char c) {
 645 
 646         // NOTE that we can't use the java.lang.Character
 647         // isUpperCase, isLowerCase, etc. methods since they
 648         // include many characters other than the Alphabetic list in
 649         // the CORBA spec.  Instead, we test for inclusion in the
 650         // Unicode value ranges for the corresponding legal characters.
 651 
 652         boolean alphaChar =
 653             (
 654              // A - Z
 655              ((c >= 0x0041) && (c <= 0x005A))
 656 
 657              ||
 658 
 659              // a - z
 660              ((c >= 0x0061) && (c <= 0x007A))
 661 
 662              ||
 663 
 664              // other letter uppercase, other letter lowercase, which is
 665              // the entire upper half of C1 Controls except X and /
 666              ((c >= 0x00C0) && (c <= 0x00FF)
 667               && (c != 0x00D7) && (c != 0x00F7)));
 668 
 669         return alphaChar;
 670     }
 671 
 672     /**
 673      * True if character is one of 10 Decimal Digits
 674      * specified in Table 3 of Chapter 3 in CORBA spec.
 675      */
 676     private static boolean isIDLDecimalDigit(char c) {
 677         return ( (c >= 0x0030) && (c <= 0x0039) );
 678     }
 679 
 680     private static boolean isUnderscore(char c) {
 681         return ( c == 0x005F );
 682     }
 683 
 684     /**
 685      * Mangle an overloaded method name as defined in Section 1.3.2.6 of
 686      * Java2IDL spec.  Current value of method name is passed in as argument.
 687      * We can't start from original method name since the name might have
 688      * been partially mangled as a result of the other rules.
 689      */
 690     private static String mangleOverloadedMethod(String mangledName, Method m) {
 691 
 692         IDLTypesUtil idlTypesUtil = new IDLTypesUtil();
 693 
 694         // Start by appending the separator string
 695         String newMangledName = mangledName + OVERLOADED_TYPE_SEPARATOR;
 696 
 697         Class[] parameterTypes = m.getParameterTypes();
 698 
 699         for(int i = 0; i < parameterTypes.length; i++) {
 700             Class nextParamType = parameterTypes[i];
 701 
 702             if( i > 0 ) {
 703                 newMangledName = newMangledName + OVERLOADED_TYPE_SEPARATOR;
 704             }
 705             IDLType idlType = classToIDLType(nextParamType);
 706 
 707             String moduleName = idlType.getModuleName();
 708             String memberName = idlType.getMemberName();
 709 
 710             String typeName = (moduleName.length() > 0) ?
 711                 moduleName + UNDERSCORE + memberName : memberName;
 712 
 713             if( !idlTypesUtil.isPrimitive(nextParamType) &&
 714                 (idlTypesUtil.getSpecialCaseIDLTypeMapping(nextParamType)
 715                  == null) &&
 716                 isIDLKeyword(typeName) ) {
 717                 typeName = mangleIDLKeywordClash(typeName);
 718             }
 719 
 720             typeName = mangleUnicodeChars(typeName);
 721 
 722             newMangledName = newMangledName + typeName;
 723         }
 724 
 725         return newMangledName;
 726     }
 727 
 728 
 729     private static IDLType classToIDLType(Class c) {
 730 
 731         IDLType idlType = null;
 732         IDLTypesUtil idlTypesUtil = new IDLTypesUtil();
 733 
 734         if( idlTypesUtil.isPrimitive(c) ) {
 735 
 736             idlType = idlTypesUtil.getPrimitiveIDLTypeMapping(c);
 737 
 738         } else if( c.isArray() ) {
 739 
 740             // Calculate array depth, as well as base element type.
 741             Class componentType = c.getComponentType();
 742             int numArrayDimensions = 1;
 743             while(componentType.isArray()) {
 744                 componentType = componentType.getComponentType();
 745                 numArrayDimensions++;
 746             }
 747             IDLType componentIdlType = classToIDLType(componentType);
 748 
 749             String[] modules = BASE_IDL_ARRAY_MODULE_TYPE;
 750             if( componentIdlType.hasModule() ) {
 751                 modules = (String[])ObjectUtility.concatenateArrays( modules,
 752                     componentIdlType.getModules() ) ;
 753             }
 754 
 755             String memberName = BASE_IDL_ARRAY_ELEMENT_TYPE +
 756                 numArrayDimensions + UNDERSCORE +
 757                 componentIdlType.getMemberName();
 758 
 759             idlType = new IDLType(c, modules, memberName);
 760 
 761         } else {
 762             idlType = idlTypesUtil.getSpecialCaseIDLTypeMapping(c);
 763 
 764             if (idlType == null) {
 765                 // Section 1.3.2.5 of Java2IDL spec defines mangling rules for
 766                 // inner classes.
 767                 String memberName = getUnmappedContainerName(c);
 768 
 769                 // replace inner class separator with double underscore
 770                 memberName = memberName.replaceAll("\\$",
 771                                                    INNER_CLASS_SEPARATOR);
 772 
 773                 if( hasLeadingUnderscore(memberName) ) {
 774                     memberName = mangleLeadingUnderscore(memberName);
 775                 }
 776 
 777                 // Get raw package name.  If there is a package, it
 778                 // will still have the "." separators and none of the
 779                 // mangling rules will have been applied.
 780                 String packageName = getPackageName(c);
 781 
 782                 if (packageName == null) {
 783                     idlType = new IDLType( c, memberName ) ;
 784                 } else {
 785                     // If this is a generated IDL Entity Type we need to
 786                     // prepend org_omg_boxedIDL per sections 1.3.5 and 1.3.9
 787                     if (idlTypesUtil.isEntity(c)) {
 788                         packageName = "org.omg.boxedIDL." + packageName ;
 789                     }
 790 
 791                     // Section 1.3.2.1 and 1.3.2.6 of Java2IDL spec defines
 792                     // rules for mapping java packages to IDL modules and for
 793                     // mangling module name portion of type name.  NOTE that
 794                     // of the individual identifier mangling rules,
 795                     // only the leading underscore test is done here.
 796                     // The other two(IDL Keyword, Illegal Unicode chars) are
 797                     // done in mangleOverloadedMethodName.
 798                     StringTokenizer tokenizer =
 799                         new StringTokenizer(packageName, ".");
 800 
 801                     String[] modules = new String[ tokenizer.countTokens() ] ;
 802                     int index = 0 ;
 803                     while (tokenizer.hasMoreElements()) {
 804                         String next = tokenizer.nextToken();
 805                         String moreMangled = hasLeadingUnderscore( next ) ?
 806                             mangleLeadingUnderscore( next ) : next;
 807 
 808                         modules[index++] = moreMangled ;
 809                     }
 810 
 811                     idlType = new IDLType(c, modules, memberName);
 812                 }
 813             }
 814         }
 815 
 816         return idlType;
 817     }
 818 
 819     /**
 820      * Return Class' package name or null if there is no package.
 821      */
 822     private static String getPackageName(Class c) {
 823         Package thePackage = c.getPackage();
 824         String packageName = null;
 825 
 826         // Try to get package name by introspection.  Some classloaders might
 827         // not provide this information, so check for null.
 828         if( thePackage != null ) {
 829             packageName = thePackage.getName();
 830         } else {
 831             // brute force method
 832             String fullyQualifiedClassName = c.getName();
 833             int lastDot = fullyQualifiedClassName.indexOf('.');
 834             packageName = (lastDot == -1) ? null :
 835                 fullyQualifiedClassName.substring(0, lastDot);
 836         }
 837         return packageName;
 838     }
 839 
 840     private static String getMappedContainerName(Class c) {
 841         String unmappedName = getUnmappedContainerName(c);
 842 
 843         return mangleIdentifier(unmappedName);
 844     }
 845 
 846     /**
 847      * Return portion of class name excluding package name.
 848      */
 849     private static String getUnmappedContainerName(Class c) {
 850 
 851         String memberName  = null;
 852         String packageName = getPackageName(c);
 853 
 854         String fullyQualifiedClassName = c.getName();
 855 
 856         if( packageName != null ) {
 857             int packageLength = packageName.length();
 858             memberName = fullyQualifiedClassName.substring(packageLength + 1);
 859         } else {
 860             memberName = fullyQualifiedClassName;
 861 
 862         }
 863 
 864         return memberName;
 865     }
 866 
 867     /**
 868      * Internal helper class for tracking information related to each
 869      * interface method while we're building the name translation table.
 870      */
 871     private static class IDLMethodInfo
 872     {
 873         public Method method;
 874         public boolean isProperty;
 875 
 876         // If this is a property, originalName holds the original
 877         // attribute name. Otherwise, it holds the original method name.
 878         public String originalName;
 879 
 880         // If this is a property, mangledName holds the mangled attribute
 881         // name. Otherwise, it holds the mangled method name.
 882         public String mangledName;
 883 
 884     }
 885 
 886     public String toString() {
 887 
 888         StringBuffer contents = new StringBuffer();
 889         contents.append("IDLNameTranslator[" );
 890         for( int ctr=0; ctr<interf_.length; ctr++) {
 891             if (ctr != 0)
 892                 contents.append( " " ) ;
 893             contents.append( interf_[ctr].getName() ) ;
 894         }
 895         contents.append("]\n");
 896         for(Iterator iter = methodToIDLNameMap_.keySet().iterator();
 897             iter.hasNext();) {
 898 
 899             Method method  = (Method) iter.next();
 900             String idlName = (String) methodToIDLNameMap_.get(method);
 901 
 902             contents.append(idlName + ":" + method + "\n");
 903 
 904         }
 905 
 906         return contents.toString();
 907     }
 908 }