/*
* Copyright (c) 2003, 2006, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package org.jboss.com.sun.corba.se.impl.presentation.rmi;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
/**
* Utility class for testing RMI/IDL Types as defined in Section 1.2 of The Java Language to IDL Mapping. Note that
* these are static checks only. Runtime checks, such as those described in Section 1.2.3, #3, are not covered.
*/
public final class IDLTypesUtil
{
private static final String GET_PROPERTY_PREFIX = "get";
private static final String SET_PROPERTY_PREFIX = "set";
private static final String IS_PROPERTY_PREFIX = "is";
public static final int VALID_TYPE = 0;
public static final int INVALID_TYPE = 1;
/*
* rmic -iiop does not correctly implement the clause in 1.3.4.3 about is<NAME>/get<NAME> conflicts. The spec says
* that is<NAME> is the property and get<NAME> is left alone, but rmic does the opposite. We will follow rmic in
* this, but it's easy to change.
*/
public static final boolean FOLLOW_RMIC = true;
/**
* Validate a class to ensure it conforms to the rules for a Java RMI/IIOP interface.
*
* @throws IDLTypeException
* if not a valid RMI/IIOP interface.
*/
public void validateRemoteInterface(Class<?> c) throws IDLTypeException
{
if (c == null)
{
throw new IllegalArgumentException();
}
if (!c.isInterface())
{
String msg = "Class " + c + " must be a java interface.";
throw new IDLTypeException(msg);
}
if (!java.rmi.Remote.class.isAssignableFrom(c))
{
String msg = "Class " + c + " must extend java.rmi.Remote, " + "either directly or indirectly.";
throw new IDLTypeException(msg);
}
// Get all methods, including super-interface methods.
Method[] methods = c.getMethods();
for (int i = 0; i < methods.length; i++)
{
Method next = methods[i];
validateExceptions(next);
}
// Removed because of bug 4989053
// validateDirectInterfaces(c);
validateConstants(c);
return;
}
public boolean isRemoteInterface(Class<?> c)
{
boolean remoteInterface = true;
try
{
validateRemoteInterface(c);
}
catch (IDLTypeException ite)
{
remoteInterface = false;
}
return remoteInterface;
}
/**
* Section 1.2.2 Primitive Types
*/
public boolean isPrimitive(Class<?> c)
{
if (c == null)
{
throw new IllegalArgumentException();
}
return c.isPrimitive();
}
/**
* Section 1.2.4
*/
public boolean isValue(Class<?> c)
{
if (c == null)
{
throw new IllegalArgumentException();
}
return (!c.isInterface() && java.io.Serializable.class.isAssignableFrom(c) && !java.rmi.Remote.class
.isAssignableFrom(c));
}
/**
* Section 1.2.5
*/
public boolean isArray(Class<?> c)
{
boolean arrayType = false;
if (c == null)
{
throw new IllegalArgumentException();
}
if (c.isArray())
{
Class<?> componentType = c.getComponentType();
arrayType = (isPrimitive(componentType) || isRemoteInterface(componentType) || isEntity(componentType)
|| isException(componentType) || isValue(componentType) || isObjectReference(componentType));
}
return arrayType;
}
/**
* Section 1.2.6
*/
public boolean isException(Class<?> c)
{
if (c == null)
{
throw new IllegalArgumentException();
}
// Must be a checked exception, not including RemoteException or
// its subclasses.
return isCheckedException(c) && !isRemoteException(c) && isValue(c);
}
public boolean isRemoteException(Class<?> c)
{
if (c == null)
{
throw new IllegalArgumentException();
}
return java.rmi.RemoteException.class.isAssignableFrom(c);
}
public boolean isCheckedException(Class<?> c)
{
if (c == null)
{
throw new IllegalArgumentException();
}
return Throwable.class.isAssignableFrom(c) && !RuntimeException.class.isAssignableFrom(c)
&& !Error.class.isAssignableFrom(c);
}
/**
* Section 1.2.7
*/
public boolean isObjectReference(Class<?> c)
{
if (c == null)
{
throw new IllegalArgumentException();
}
return (c.isInterface() && org.omg.CORBA.Object.class.isAssignableFrom(c));
}
/**
* Section 1.2.8
*/
public boolean isEntity(Class<?> c)
{
if (c == null)
{
throw new IllegalArgumentException();
}
Class<?> superClass = c.getSuperclass();
return (!c.isInterface() && (superClass != null) && (org.omg.CORBA.portable.IDLEntity.class.isAssignableFrom(c)));
}
/**
* Return true if given method is legal property accessor as defined in Section 1.3.4.3 of Java2IDL spec.
*/
public boolean isPropertyAccessorMethod(Method m, Class<?> c)
{
String methodName = m.getName();
Class<?> returnType = m.getReturnType();
Class<?>[] parameters = m.getParameterTypes();
String propertyType = null;
if (methodName.startsWith(GET_PROPERTY_PREFIX))
{
if ((parameters.length == 0) && (returnType != Void.TYPE) && !readHasCorrespondingIsProperty(m, c))
{
propertyType = GET_PROPERTY_PREFIX;
}
}
else if (methodName.startsWith(SET_PROPERTY_PREFIX))
{
if ((returnType == Void.TYPE) && (parameters.length == 1))
{
if (hasCorrespondingReadProperty(m, c, GET_PROPERTY_PREFIX)
|| hasCorrespondingReadProperty(m, c, IS_PROPERTY_PREFIX))
{
propertyType = SET_PROPERTY_PREFIX;
}
}
}
else if (methodName.startsWith(IS_PROPERTY_PREFIX))
{
if ((parameters.length == 0) && (returnType == Boolean.TYPE) && !isHasCorrespondingReadProperty(m, c))
{
propertyType = IS_PROPERTY_PREFIX;
}
}
// Some final checks that apply to all properties.
if (propertyType != null)
{
if (!validPropertyExceptions(m) || (methodName.length() <= propertyType.length()))
{
propertyType = null;
}
}
return (propertyType != null);
}
private boolean hasCorrespondingReadProperty(Method writeProperty, Class<?> c, String readPropertyPrefix)
{
String writePropertyMethodName = writeProperty.getName();
Class<?>[] writePropertyParameters = writeProperty.getParameterTypes();
boolean foundReadProperty = false;
try
{
// Look for a valid corresponding Read property
String readPropertyMethodName = writePropertyMethodName.replaceFirst(SET_PROPERTY_PREFIX,
readPropertyPrefix);
Method readPropertyMethod = c.getMethod(readPropertyMethodName, new Class[]{});
foundReadProperty = (isPropertyAccessorMethod(readPropertyMethod, c) && (readPropertyMethod.getReturnType() == writePropertyParameters[0]));
}
catch (Exception e)
{
// ignore. this means we didn't find a corresponding get property.
}
return foundReadProperty;
}
@SuppressWarnings("unused")
private boolean readHasCorrespondingIsProperty(Method readProperty, Class<?> c)
{
if (FOLLOW_RMIC)
return false;
String readPropertyMethodName = readProperty.getName();
boolean foundIsProperty = false;
try
{
// Look for a valid corresponding Is property
String isPropertyMethodName = readPropertyMethodName.replaceFirst(GET_PROPERTY_PREFIX, IS_PROPERTY_PREFIX);
Method isPropertyMethod = c.getMethod(isPropertyMethodName, new Class[]{});
foundIsProperty = isPropertyAccessorMethod(isPropertyMethod, c);
}
catch (Exception e)
{
// ignore. this means we didn't find a corresponding Is property.
}
return foundIsProperty;
}
private boolean isHasCorrespondingReadProperty(Method readProperty, Class<?> c)
{
if (!FOLLOW_RMIC)
return false;
String readPropertyMethodName = readProperty.getName();
boolean foundIsProperty = false;
try
{
// Look for a valid corresponding Read property
String isPropertyMethodName = readPropertyMethodName.replaceFirst(IS_PROPERTY_PREFIX, GET_PROPERTY_PREFIX);
Method isPropertyMethod = c.getMethod(isPropertyMethodName, new Class[]{});
foundIsProperty = isPropertyAccessorMethod(isPropertyMethod, c);
}
catch (Exception e)
{
// ignore. this means we didn't find a corresponding read property.
}
return foundIsProperty;
}
public String getAttributeNameForProperty(String propertyName)
{
String attributeName = null;
String prefix = null;
if (propertyName.startsWith(GET_PROPERTY_PREFIX))
{
prefix = GET_PROPERTY_PREFIX;
}
else if (propertyName.startsWith(SET_PROPERTY_PREFIX))
{
prefix = SET_PROPERTY_PREFIX;
}
else if (propertyName.startsWith(IS_PROPERTY_PREFIX))
{
prefix = IS_PROPERTY_PREFIX;
}
if ((prefix != null) && (prefix.length() < propertyName.length()))
{
String remainder = propertyName.substring(prefix.length());
if ((remainder.length() >= 2) && Character.isUpperCase(remainder.charAt(0))
&& Character.isUpperCase(remainder.charAt(1)))
{
// don't set the first letter to lower-case if the
// first two are upper-case
attributeName = remainder;
}
else
{
attributeName = Character.toLowerCase(remainder.charAt(0)) + remainder.substring(1);
}
}
return attributeName;
}
/**
* Return IDL Type name for primitive types as defined in Section 1.3.3 of Java2IDL spec or null if not a primitive
* type.
*/
public IDLType getPrimitiveIDLTypeMapping(Class<?> c)
{
if (c == null)
{
throw new IllegalArgumentException();
}
if (c.isPrimitive())
{
if (c == Void.TYPE)
{
return new IDLType(c, "void");
}
else if (c == Boolean.TYPE)
{
return new IDLType(c, "boolean");
}
else if (c == Character.TYPE)
{
return new IDLType(c, "wchar");
}
else if (c == Byte.TYPE)
{
return new IDLType(c, "octet");
}
else if (c == Short.TYPE)
{
return new IDLType(c, "short");
}
else if (c == Integer.TYPE)
{
return new IDLType(c, "long");
}
else if (c == Long.TYPE)
{
return new IDLType(c, "long_long");
}
else if (c == Float.TYPE)
{
return new IDLType(c, "float");
}
else if (c == Double.TYPE)
{
return new IDLType(c, "double");
}
}
return null;
}
/**
* Return IDL Type name for special case type mappings as defined in Table 1-1 of Java2IDL spec or null if given
* class is not a special type.
*/
public IDLType getSpecialCaseIDLTypeMapping(Class<?> c)
{
if (c == null)
{
throw new IllegalArgumentException();
}
if (c == java.lang.Object.class)
{
return new IDLType(c, new String[]{"java", "lang"}, "Object");
}
else if (c == java.lang.String.class)
{
return new IDLType(c, new String[]{"CORBA"}, "WStringValue");
}
else if (c == java.lang.Class.class)
{
return new IDLType(c, new String[]{"javax", "rmi", "CORBA"}, "ClassDesc");
}
else if (c == java.io.Serializable.class)
{
return new IDLType(c, new String[]{"java", "io"}, "Serializable");
}
else if (c == java.io.Externalizable.class)
{
return new IDLType(c, new String[]{"java", "io"}, "Externalizable");
}
else if (c == java.rmi.Remote.class)
{
return new IDLType(c, new String[]{"java", "rmi"}, "Remote");
}
else if (c == org.omg.CORBA.Object.class)
{
return new IDLType(c, "Object");
}
else
{
return null;
}
}
/**
* Implements 1.2.3 #2 and #4
*/
private void validateExceptions(Method method) throws IDLTypeException
{
Class<?>[] exceptions = method.getExceptionTypes();
boolean declaresRemoteExceptionOrSuperClass = false;
// Section 1.2.3, #2
for (int eIndex = 0; eIndex < exceptions.length; eIndex++)
{
Class<?> exception = exceptions[eIndex];
if (isRemoteExceptionOrSuperClass(exception))
{
declaresRemoteExceptionOrSuperClass = true;
break;
}
}
if (!declaresRemoteExceptionOrSuperClass)
{
String msg = "Method '" + method + "' must throw at least one "
+ "exception of type java.rmi.RemoteException or one of its " + "super-classes";
throw new IDLTypeException(msg);
}
// Section 1.2.3, #4
// See also bug 4972402
// For all exceptions E in exceptions,
// (isCheckedException(E) => (isValue(E) || RemoteException.isAssignableFrom( E ) )
for (int eIndex = 0; eIndex < exceptions.length; eIndex++)
{
Class<?> exception = exceptions[eIndex];
if (isCheckedException(exception) && !isValue(exception) && !isRemoteException(exception))
{
String msg = "Exception '" + exception + "' on method '" + method
+ "' is not a allowed RMI/IIOP exception type";
throw new IDLTypeException(msg);
}
}
return;
}
/**
* Returns true if the method's throw clause conforms to the exception restrictions for properties as defined in
* Section 1.3.4.3 of Java2IDL spec. This means that for all exceptions E declared on the method, E isChecked =>
* RemoteException.isAssignableFrom( E ).
*/
private boolean validPropertyExceptions(Method method)
{
Class<?>[] exceptions = method.getExceptionTypes();
for (int eIndex = 0; eIndex < exceptions.length; eIndex++)
{
Class<?> exception = exceptions[eIndex];
if (isCheckedException(exception) && !isRemoteException(exception))
return false;
}
return true;
}
/**
* Implements Section 1.2.3, #2.
*/
private boolean isRemoteExceptionOrSuperClass(Class<?> c)
{
return ((c == java.rmi.RemoteException.class) || (c == java.io.IOException.class)
|| (c == java.lang.Exception.class) || (c == java.lang.Throwable.class));
}
/**
* Implements 1.2.3 #6
*/
private void validateConstants(final Class<?> c) throws IDLTypeException
{
Field[] fields = null;
try
{
fields = java.security.AccessController.doPrivileged(new java.security.PrivilegedExceptionAction<Field[]>()
{
public Field[] run() throws Exception
{
return c.getFields();
}
});
}
catch (java.security.PrivilegedActionException pae)
{
IDLTypeException ite = new IDLTypeException();
ite.initCause(pae);
throw ite;
}
for (int i = 0; i < fields.length; i++)
{
Field next = fields[i];
Class<?> fieldType = next.getType();
if ((fieldType != java.lang.String.class) && !isPrimitive(fieldType))
{
String msg = "Constant field '" + next.getName() + "' in class '" + next.getDeclaringClass().getName()
+ "' has invalid type' " + next.getType() + "'. Constants"
+ " in RMI/IIOP interfaces can only have primitive" + " types and java.lang.String types.";
throw new IDLTypeException(msg);
}
}
return;
}
}