1. /*
  2. * @(#)IDLTypesUtil.java 1.5 04/06/21
  3. *
  4. * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
  5. * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
  6. */
  7. package com.sun.corba.se.impl.presentation.rmi ;
  8. import java.lang.reflect.Method;
  9. import java.lang.reflect.Field;
  10. import java.util.Set;
  11. import java.util.HashSet;
  12. import java.util.Iterator;
  13. /**
  14. * Utility class for testing RMI/IDL Types as defined in
  15. * Section 1.2 of The Java Language to IDL Mapping. Note that
  16. * these are static checks only. Runtime checks, such as those
  17. * described in Section 1.2.3, #3, are not covered.
  18. */
  19. public class IDLTypesUtil {
  20. private static final String GET_PROPERTY_PREFIX = "get";
  21. private static final String SET_PROPERTY_PREFIX = "set";
  22. private static final String IS_PROPERTY_PREFIX = "is";
  23. public static final int VALID_TYPE = 0;
  24. public static final int INVALID_TYPE = 1;
  25. /* rmic -iiop does not correctly implement the clause in 1.3.4.3
  26. * about is<NAME>/get<NAME> conflicts. The spec says that
  27. * is<NAME> is the property and get<NAME> is left alone,
  28. * but rmic does the opposite. We will follow rmic in this,
  29. * but it's easy to change.
  30. */
  31. public static final boolean FOLLOW_RMIC = true ;
  32. /**
  33. * Validate a class to ensure it conforms to the rules for a
  34. * Java RMI/IIOP interface.
  35. *
  36. * @throws IDLTypeException if not a valid RMI/IIOP interface.
  37. */
  38. public void validateRemoteInterface(Class c) throws IDLTypeException
  39. {
  40. if( c == null ) {
  41. throw new IllegalArgumentException();
  42. }
  43. if( !c.isInterface() ) {
  44. String msg = "Class " + c + " must be a java interface.";
  45. throw new IDLTypeException(msg);
  46. }
  47. if( !java.rmi.Remote.class.isAssignableFrom(c) ) {
  48. String msg = "Class " + c + " must extend java.rmi.Remote, " +
  49. "either directly or indirectly.";
  50. throw new IDLTypeException(msg);
  51. }
  52. // Get all methods, including super-interface methods.
  53. Method[] methods = c.getMethods();
  54. for(int i = 0; i < methods.length; i++) {
  55. Method next = methods[i];
  56. validateExceptions(next);
  57. }
  58. // Removed because of bug 4989053
  59. // validateDirectInterfaces(c);
  60. validateConstants(c);
  61. return;
  62. }
  63. public boolean isRemoteInterface(Class c)
  64. {
  65. boolean remoteInterface = true;
  66. try {
  67. validateRemoteInterface(c);
  68. } catch(IDLTypeException ite) {
  69. remoteInterface = false;
  70. }
  71. return remoteInterface;
  72. }
  73. /**
  74. * Section 1.2.2 Primitive Types
  75. */
  76. public boolean isPrimitive(Class c)
  77. {
  78. if( c == null ) {
  79. throw new IllegalArgumentException();
  80. }
  81. return c.isPrimitive();
  82. }
  83. /**
  84. * Section 1.2.4
  85. */
  86. public boolean isValue(Class c)
  87. {
  88. if( c == null ) {
  89. throw new IllegalArgumentException();
  90. }
  91. return
  92. (!c.isInterface() &&
  93. java.io.Serializable.class.isAssignableFrom(c) &&
  94. !java.rmi.Remote.class.isAssignableFrom(c));
  95. }
  96. /**
  97. * Section 1.2.5
  98. */
  99. public boolean isArray(Class c)
  100. {
  101. boolean arrayType = false;
  102. if( c == null ) {
  103. throw new IllegalArgumentException();
  104. }
  105. if( c.isArray() ) {
  106. Class componentType = c.getComponentType();
  107. arrayType =
  108. (isPrimitive(componentType) || isRemoteInterface(componentType) ||
  109. isEntity(componentType) || isException(componentType) ||
  110. isValue(componentType) || isObjectReference(componentType) );
  111. }
  112. return arrayType;
  113. }
  114. /**
  115. * Section 1.2.6
  116. */
  117. public boolean isException(Class c)
  118. {
  119. if( c == null ) {
  120. throw new IllegalArgumentException();
  121. }
  122. // Must be a checked exception, not including RemoteException or
  123. // its subclasses.
  124. return isCheckedException(c) && !isRemoteException(c) && isValue(c);
  125. }
  126. public boolean isRemoteException(Class c)
  127. {
  128. if( c == null ) {
  129. throw new IllegalArgumentException();
  130. }
  131. return java.rmi.RemoteException.class.isAssignableFrom(c) ;
  132. }
  133. public boolean isCheckedException(Class c)
  134. {
  135. if( c == null ) {
  136. throw new IllegalArgumentException();
  137. }
  138. return Throwable.class.isAssignableFrom(c) &&
  139. !RuntimeException.class.isAssignableFrom(c) &&
  140. !Error.class.isAssignableFrom(c) ;
  141. }
  142. /**
  143. * Section 1.2.7
  144. */
  145. public boolean isObjectReference(Class c)
  146. {
  147. if( c == null ) {
  148. throw new IllegalArgumentException();
  149. }
  150. return (c.isInterface() &&
  151. org.omg.CORBA.Object.class.isAssignableFrom(c));
  152. }
  153. /**
  154. * Section 1.2.8
  155. */
  156. public boolean isEntity(Class c)
  157. {
  158. if( c == null ) {
  159. throw new IllegalArgumentException();
  160. }
  161. Class superClass = c.getSuperclass();
  162. return (!c.isInterface() &&
  163. (superClass != null) &&
  164. (org.omg.CORBA.portable.IDLEntity.class.isAssignableFrom(c)));
  165. }
  166. /**
  167. * Return true if given method is legal property accessor as defined in
  168. * Section 1.3.4.3 of Java2IDL spec.
  169. */
  170. public boolean isPropertyAccessorMethod(Method m, Class c) {
  171. String methodName = m.getName();
  172. Class returnType = m.getReturnType();
  173. Class[] parameters = m.getParameterTypes();
  174. Class[] exceptionTypes = m.getExceptionTypes();
  175. String propertyType = null;
  176. if( methodName.startsWith(GET_PROPERTY_PREFIX) ) {
  177. if((parameters.length == 0) && (returnType != Void.TYPE) &&
  178. !readHasCorrespondingIsProperty(m, c)) {
  179. propertyType = GET_PROPERTY_PREFIX;
  180. }
  181. } else if( methodName.startsWith(SET_PROPERTY_PREFIX) ) {
  182. if((returnType == Void.TYPE) && (parameters.length == 1)) {
  183. if (hasCorrespondingReadProperty(m, c, GET_PROPERTY_PREFIX) ||
  184. hasCorrespondingReadProperty(m, c, IS_PROPERTY_PREFIX)) {
  185. propertyType = SET_PROPERTY_PREFIX;
  186. }
  187. }
  188. } else if( methodName.startsWith(IS_PROPERTY_PREFIX) ) {
  189. if((parameters.length == 0) && (returnType == Boolean.TYPE) &&
  190. !isHasCorrespondingReadProperty(m, c)) {
  191. propertyType = IS_PROPERTY_PREFIX;
  192. }
  193. }
  194. // Some final checks that apply to all properties.
  195. if( propertyType != null ) {
  196. if(!validPropertyExceptions(m) ||
  197. (methodName.length() <= propertyType.length())) {
  198. propertyType = null;
  199. }
  200. }
  201. return (propertyType != null);
  202. }
  203. private boolean hasCorrespondingReadProperty
  204. (Method writeProperty, Class c, String readPropertyPrefix)
  205. {
  206. String writePropertyMethodName = writeProperty.getName();
  207. Class[] writePropertyParameters = writeProperty.getParameterTypes();
  208. boolean foundReadProperty = false;
  209. try {
  210. // Look for a valid corresponding Read property
  211. String readPropertyMethodName =
  212. writePropertyMethodName.replaceFirst
  213. (SET_PROPERTY_PREFIX, readPropertyPrefix);
  214. Method readPropertyMethod = c.getMethod(readPropertyMethodName,
  215. new Class[] {});
  216. foundReadProperty =
  217. ( isPropertyAccessorMethod(readPropertyMethod, c) &&
  218. (readPropertyMethod.getReturnType() ==
  219. writePropertyParameters[0]) );
  220. } catch(Exception e) {
  221. // ignore. this means we didn't find a corresponding get property.
  222. }
  223. return foundReadProperty;
  224. }
  225. private boolean readHasCorrespondingIsProperty(Method readProperty,
  226. Class c)
  227. {
  228. if (FOLLOW_RMIC)
  229. return false ;
  230. String readPropertyMethodName = readProperty.getName();
  231. boolean foundIsProperty = false;
  232. try {
  233. // Look for a valid corresponding Is property
  234. String isPropertyMethodName =
  235. readPropertyMethodName.replaceFirst(GET_PROPERTY_PREFIX,
  236. IS_PROPERTY_PREFIX);
  237. Method isPropertyMethod = c.getMethod( isPropertyMethodName,
  238. new Class[] {});
  239. foundIsProperty = isPropertyAccessorMethod(isPropertyMethod,
  240. c) ;
  241. } catch(Exception e) {
  242. // ignore. this means we didn't find a corresponding Is property.
  243. }
  244. return foundIsProperty;
  245. }
  246. private boolean isHasCorrespondingReadProperty(Method readProperty,
  247. Class c)
  248. {
  249. if (!FOLLOW_RMIC)
  250. return false ;
  251. String readPropertyMethodName = readProperty.getName();
  252. boolean foundIsProperty = false;
  253. try {
  254. // Look for a valid corresponding Read property
  255. String isPropertyMethodName =
  256. readPropertyMethodName.replaceFirst(IS_PROPERTY_PREFIX,
  257. GET_PROPERTY_PREFIX);
  258. Method isPropertyMethod = c.getMethod( isPropertyMethodName,
  259. new Class[] {});
  260. foundIsProperty = isPropertyAccessorMethod(isPropertyMethod,
  261. c) ;
  262. } catch(Exception e) {
  263. // ignore. this means we didn't find a corresponding read property.
  264. }
  265. return foundIsProperty;
  266. }
  267. public String getAttributeNameForProperty(String propertyName) {
  268. String attributeName = null;
  269. String prefix = null;
  270. if( propertyName.startsWith(GET_PROPERTY_PREFIX) ) {
  271. prefix = GET_PROPERTY_PREFIX;
  272. } else if( propertyName.startsWith(SET_PROPERTY_PREFIX) ) {
  273. prefix = SET_PROPERTY_PREFIX;
  274. } else if( propertyName.startsWith(IS_PROPERTY_PREFIX) ) {
  275. prefix = IS_PROPERTY_PREFIX;
  276. }
  277. if( (prefix != null) && (prefix.length() < propertyName.length()) ) {
  278. String remainder = propertyName.substring(prefix.length());
  279. if( (remainder.length() >= 2) &&
  280. Character.isUpperCase(remainder.charAt(0)) &&
  281. Character.isUpperCase(remainder.charAt(1)) ) {
  282. // don't set the first letter to lower-case if the
  283. // first two are upper-case
  284. attributeName = remainder;
  285. } else {
  286. attributeName = Character.toLowerCase(remainder.charAt(0)) +
  287. remainder.substring(1);
  288. }
  289. }
  290. return attributeName;
  291. }
  292. /**
  293. * Return IDL Type name for primitive types as defined in
  294. * Section 1.3.3 of Java2IDL spec or null if not a primitive type.
  295. */
  296. public IDLType getPrimitiveIDLTypeMapping(Class c) {
  297. if( c == null ) {
  298. throw new IllegalArgumentException();
  299. }
  300. if( c.isPrimitive() ) {
  301. if( c == Void.TYPE ) {
  302. return new IDLType( c, "void" ) ;
  303. } else if( c == Boolean.TYPE ) {
  304. return new IDLType( c, "boolean" ) ;
  305. } else if( c == Character.TYPE ) {
  306. return new IDLType( c, "wchar" ) ;
  307. } else if( c == Byte.TYPE ) {
  308. return new IDLType( c, "octet" ) ;
  309. } else if( c == Short.TYPE ) {
  310. return new IDLType( c, "short" ) ;
  311. } else if( c == Integer.TYPE ) {
  312. return new IDLType( c, "long" ) ;
  313. } else if( c == Long.TYPE ) {
  314. return new IDLType( c, "long_long" ) ;
  315. } else if( c == Float.TYPE ) {
  316. return new IDLType( c, "float" ) ;
  317. } else if( c == Double.TYPE ) {
  318. return new IDLType( c, "double" ) ;
  319. }
  320. }
  321. return null;
  322. }
  323. /**
  324. * Return IDL Type name for special case type mappings as defined in
  325. * Table 1-1 of Java2IDL spec or null if given class is not a special
  326. * type.
  327. */
  328. public IDLType getSpecialCaseIDLTypeMapping(Class c) {
  329. if( c == null ) {
  330. throw new IllegalArgumentException();
  331. }
  332. if( c == java.lang.Object.class ) {
  333. return new IDLType( c, new String[] { "java", "lang" },
  334. "Object" ) ;
  335. } else if( c == java.lang.String.class ) {
  336. return new IDLType( c, new String[] { "CORBA" },
  337. "WStringValue" ) ;
  338. } else if( c == java.lang.Class.class ) {
  339. return new IDLType( c, new String[] { "javax", "rmi", "CORBA" },
  340. "ClassDesc" ) ;
  341. } else if( c == java.io.Serializable.class ) {
  342. return new IDLType( c, new String[] { "java", "io" },
  343. "Serializable" ) ;
  344. } else if( c == java.io.Externalizable.class ) {
  345. return new IDLType( c, new String[] { "java", "io" },
  346. "Externalizable" ) ;
  347. } else if( c == java.rmi.Remote.class ) {
  348. return new IDLType( c, new String[] { "java", "rmi" },
  349. "Remote" ) ;
  350. } else if( c == org.omg.CORBA.Object.class ) {
  351. return new IDLType( c, "Object" ) ;
  352. } else {
  353. return null;
  354. }
  355. }
  356. /**
  357. * Implements 1.2.3 #2 and #4
  358. */
  359. private void validateExceptions(Method method) throws IDLTypeException {
  360. Class[] exceptions = method.getExceptionTypes();
  361. boolean declaresRemoteExceptionOrSuperClass = false;
  362. // Section 1.2.3, #2
  363. for(int eIndex = 0; eIndex < exceptions.length; eIndex++) {
  364. Class exception = exceptions[eIndex];
  365. if( isRemoteExceptionOrSuperClass(exception) ) {
  366. declaresRemoteExceptionOrSuperClass = true;
  367. break;
  368. }
  369. }
  370. if( !declaresRemoteExceptionOrSuperClass ) {
  371. String msg = "Method '" + method + "' must throw at least one " +
  372. "exception of type java.rmi.RemoteException or one of its " +
  373. "super-classes";
  374. throw new IDLTypeException(msg);
  375. }
  376. // Section 1.2.3, #4
  377. // See also bug 4972402
  378. // For all exceptions E in exceptions,
  379. // (isCheckedException(E) => (isValue(E) || RemoteException.isAssignableFrom( E ) )
  380. for(int eIndex = 0; eIndex < exceptions.length; eIndex++) {
  381. Class exception = exceptions[eIndex];
  382. if (isCheckedException(exception) && !isValue(exception) &&
  383. !isRemoteException(exception))
  384. {
  385. String msg = "Exception '" + exception + "' on method '" +
  386. method + "' is not a allowed RMI/IIOP exception type";
  387. throw new IDLTypeException(msg);
  388. }
  389. }
  390. return;
  391. }
  392. /**
  393. * Returns true if the method's throw clause conforms to the exception
  394. * restrictions for properties as defined in Section 1.3.4.3 of
  395. * Java2IDL spec. This means that for all exceptions E declared on the
  396. * method, E isChecked => RemoteException.isAssignableFrom( E ).
  397. */
  398. private boolean validPropertyExceptions(Method method)
  399. {
  400. Class[] exceptions = method.getExceptionTypes();
  401. for(int eIndex = 0; eIndex < exceptions.length; eIndex++) {
  402. Class exception = exceptions[eIndex];
  403. if (isCheckedException(exception) && !isRemoteException(exception))
  404. return false ;
  405. }
  406. return true;
  407. }
  408. /**
  409. * Implements Section 1.2.3, #2.
  410. */
  411. private boolean isRemoteExceptionOrSuperClass(Class c) {
  412. return
  413. ((c == java.rmi.RemoteException.class) ||
  414. (c == java.io.IOException.class) ||
  415. (c == java.lang.Exception.class) ||
  416. (c == java.lang.Throwable.class));
  417. }
  418. /**
  419. * Implements Section 1.2.3, #5.
  420. */
  421. private void validateDirectInterfaces(Class c) throws IDLTypeException {
  422. Class[] directInterfaces = c.getInterfaces();
  423. if( directInterfaces.length < 2 ) {
  424. return;
  425. }
  426. Set allMethodNames = new HashSet();
  427. Set currentMethodNames = new HashSet();
  428. for(int i = 0; i < directInterfaces.length; i++) {
  429. Class next = directInterfaces[i];
  430. Method[] methods = next.getMethods();
  431. // Comparison is based on method names only. First collect
  432. // all methods from current interface, eliminating duplicate
  433. // names.
  434. currentMethodNames.clear();
  435. for(int m = 0; m < methods.length; m++) {
  436. currentMethodNames.add(methods[m].getName());
  437. }
  438. // Now check each method against list of all unique method
  439. // names processed so far.
  440. for(Iterator iter=currentMethodNames.iterator(); iter.hasNext();) {
  441. String methodName = (String) iter.next();
  442. if( allMethodNames.contains(methodName) ) {
  443. String msg = "Class " + c + " inherits method " +
  444. methodName + " from multiple direct interfaces.";
  445. throw new IDLTypeException(msg);
  446. } else {
  447. allMethodNames.add(methodName);
  448. }
  449. }
  450. }
  451. return;
  452. }
  453. /**
  454. * Implements 1.2.3 #6
  455. */
  456. private void validateConstants(final Class c)
  457. throws IDLTypeException {
  458. Field[] fields = null;
  459. try {
  460. fields = (Field[])
  461. java.security.AccessController.doPrivileged
  462. (new java.security.PrivilegedExceptionAction() {
  463. public java.lang.Object run() throws Exception {
  464. return c.getFields();
  465. }
  466. });
  467. } catch(java.security.PrivilegedActionException pae) {
  468. IDLTypeException ite = new IDLTypeException();
  469. ite.initCause(pae);
  470. throw ite;
  471. }
  472. for(int i = 0; i < fields.length; i++) {
  473. Field next = fields[i];
  474. Class fieldType = next.getType();
  475. if( (fieldType != java.lang.String.class) &&
  476. !isPrimitive(fieldType) ) {
  477. String msg = "Constant field '" + next.getName() +
  478. "' in class '" + next.getDeclaringClass().getName() +
  479. "' has invalid type' " + next.getType() + "'. Constants" +
  480. " in RMI/IIOP interfaces can only have primitive" +
  481. " types and java.lang.String types.";
  482. throw new IDLTypeException(msg);
  483. }
  484. }
  485. return;
  486. }
  487. }