/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.tuscany.sca.binding.ejb.java2idl;
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.Externalizable;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.io.ObjectStreamClass;
import java.io.Serializable;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.rmi.Remote;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;
import java.util.WeakHashMap;
import org.omg.CORBA.Any;
/**
* This is a RMI/IIOP metadata conversion utility class.
*/
public class IDLUtil {
/**
* Return the IDL type name for the given class. Here we use the mapping for
* parameter types and return values.
*/
public static String getTypeIDLName(Class cls) {
if (cls.isPrimitive())
return PrimitiveType.getPrimitiveType(cls).getIDLName();
if (cls.isArray()) {
int dimension = 0;
Class type = cls;
while (type.isArray()) {
dimension++;
type = type.getComponentType();
}
String baseName = getTypeIDLName(type);
int index = baseName.lastIndexOf("::");
String prefix = "";
String suffix = baseName;
if (index != -1) {
prefix = baseName.substring(0, index);
suffix = baseName.substring(index + 2);
}
String name = "::org::omg::boxedRMI" + prefix + "::seq" + dimension + "_" + suffix;
return name;
}
// special classes
if (cls == java.lang.String.class)
return "::CORBA::WStringValue";
if (cls == java.lang.Object.class)
return "::java::lang::_Object";
if (cls == java.lang.Class.class)
return "::javax::rmi::CORBA::ClassDesc";
if (cls == java.io.Serializable.class)
return "::java::io::Serializable";
if (cls == java.io.Externalizable.class)
return "::java::io::Externalizable";
if (cls == java.rmi.Remote.class)
return "::java::rmi::Remote";
if (cls == org.omg.CORBA.Object.class)
return "::CORBA::Object";
// remote interface?
if (cls.isInterface() && java.rmi.Remote.class.isAssignableFrom(cls)) {
InterfaceType ia = InterfaceType.getInterfaceType(cls);
return ia.getIDLModuleName() + "::" + ia.getIDLName();
}
// IDL interface?
if (cls.isInterface() && org.omg.CORBA.Object.class.isAssignableFrom(cls)
&& org.omg.CORBA.portable.IDLEntity.class.isAssignableFrom(cls)) {
InterfaceType ia = InterfaceType.getInterfaceType(cls);
return ia.getIDLModuleName() + "::" + ia.getIDLName();
}
// exception?
if (Throwable.class.isAssignableFrom(cls)) {
if (Exception.class.isAssignableFrom(cls) && !RuntimeException.class.isAssignableFrom(cls)) {
ExceptionType ea = ExceptionType.getExceptionType(cls);
return ea.getIDLModuleName() + "::" + ea.getIDLName();
}
}
// got to be value
ValueType va = ValueType.getValueType(cls);
return va.getIDLModuleName() + "::" + va.getIDLName();
}
/**
* Check if this class is valid for RMI/IIOP mapping. This method will
* either throw an exception or return true.
*/
public static boolean isValidRMIIIOP(Class cls) {
if (cls.isPrimitive())
return true;
if (cls.isArray())
return isValidRMIIIOP(cls.getComponentType());
// special interfaces
if (cls == Serializable.class || cls == Externalizable.class)
return true;
// interface?
if (cls.isInterface() && Remote.class.isAssignableFrom(cls)) {
InterfaceType.getInterfaceType(cls);
return true;
}
// exception?
if (Throwable.class.isAssignableFrom(cls)) {
if (Exception.class.isAssignableFrom(cls) && (!RuntimeException.class.isAssignableFrom(cls))) {
ExceptionType.getExceptionType(cls);
}
return true;
}
// special values
if (cls == Object.class || cls == String.class || cls == Class.class)
return true;
// got to be value
ValueType.getValueType(cls);
return true;
}
/**
* Insert a java primitive into an Any. The primitive is assumed to be
* wrapped in one of the primitive wrapper classes.
*/
public static void insertAnyPrimitive(Any any, Object primitive) {
Class type = primitive.getClass();
if (type == Boolean.class)
any.insert_boolean(((Boolean)primitive).booleanValue());
else if (type == Character.class)
any.insert_wchar(((Character)primitive).charValue());
else if (type == Byte.class)
any.insert_octet(((Byte)primitive).byteValue());
else if (type == Short.class)
any.insert_short(((Short)primitive).shortValue());
else if (type == Integer.class)
any.insert_long(((Integer)primitive).intValue());
else if (type == Long.class)
any.insert_longlong(((Long)primitive).longValue());
else if (type == Float.class)
any.insert_float(((Float)primitive).floatValue());
else if (type == Double.class)
any.insert_double(((Double)primitive).doubleValue());
else
throw new IllegalArgumentException(type.getName() + "is not a primitive type");
}
/**
* Map Java name to IDL name, as per sections 1.3.2.3, 1.3.2.4 and 1.3.2.2.
* This only works for a single name component, without a qualifying dot.
*/
public static String javaToIDLName(String name) {
if (name == null || "".equals(name))
throw new IllegalArgumentException("Illegal name: " + name);
if (name.indexOf('.') != -1)
throw new IllegalArgumentException("Qualified name is not supported: " + name);
StringBuffer res = new StringBuffer(name.length());
if (name.charAt(0) == '_')
res.append('J'); // 1.3.2.3
for (int i = 0; i < name.length(); ++i) {
char c = name.charAt(i);
if (isLegalIDLIdentifierChar(c))
res.append(c);
else
// 1.3.2.4
res.append('U').append(toHexString((int)c));
}
String s = res.toString();
if (isReservedIDLKeyword(s))
return "_" + s;
else
return s;
}
/**
* Return the IR global ID of the given class or interface. This is
* described in section 1.3.5.7.
*/
public static String getIRIdentifier(Class cls) {
if (cls.isPrimitive())
throw new IllegalArgumentException("Primitive type doesn't have IR IDs.");
String result = (String)classIRIdentifierCache.get(cls);
if (result != null)
return result;
String name = cls.getName();
StringBuffer b = new StringBuffer("RMI:");
for (int i = 0; i < name.length(); ++i) {
char c = name.charAt(i);
if (c < 256)
b.append(c);
else
b.append("\\U").append(toHexString((int)c));
}
long clsHash = getClassHashCode(cls);
b.append(':').append(toHexString(clsHash));
ObjectStreamClass osClass = ObjectStreamClass.lookup(cls);
if (osClass != null) {
long serialVersionUID = osClass.getSerialVersionUID();
if (clsHash != serialVersionUID)
b.append(':').append(toHexString(serialVersionUID));
}
result = b.toString();
classIRIdentifierCache.put(cls, result);
return result;
}
/**
* A cache for calculated class hash codes.
*/
private static Map classHashCodeCache = Collections.synchronizedMap(new WeakHashMap());
/**
* A cache for class IR identifiers.
*/
private static Map classIRIdentifierCache = Collections.synchronizedMap(new WeakHashMap());
/**
* Reserved IDL keywords. Section 1.3.2.2 says that Java identifiers with
* these names should have prepended an underscore.
*/
private static final Set reservedIDLKeywordSet = new HashSet();
static {
String[] reservedIDLKeywords = new String[] {"abstract", "any", "attribute", "boolean", "case", "char",
"const", "context", "custom", "default", "double", "exception",
"enum", "factory", "FALSE", "fixed", "float", "in", "inout",
"interface", "local", "long", "module", "native", "Object",
"octet", "oneway", "out", "private", "public", "raises",
"readonly", "sequence", "short", "string", "struct", "supports",
"switch", "TRUE", "truncatable", "typedef", "unsigned", "union",
"ValueBase", "valuetype", "void", "wchar", "wstring"};
for (int i = 0; i < reservedIDLKeywords.length; i++)
reservedIDLKeywordSet.add(reservedIDLKeywords[i]);
}
/**
* Convert an integer to a 16-digit hex string.
*/
private static String toHexString(int i) {
String s = Integer.toHexString(i).toUpperCase();
if (s.length() < 8)
return "00000000".substring(8 - s.length()) + s;
else
return s;
}
/**
* Convert a long to a 16-digit hex string.
*/
private static String toHexString(long l) {
String s = Long.toHexString(l).toUpperCase();
if (s.length() < 16)
return "0000000000000000".substring(16 - s.length()) + s;
else
return s;
}
/**
* Determine if the argument is a reserved IDL keyword.
*/
private static boolean isReservedIDLKeyword(String s) {
return reservedIDLKeywordSet.contains(s);
/*
* // TODO: faster lookup for (int i = 0; i <
* reservedIDLKeywords.length; ++i) if
* (reservedIDLKeywords[i].equals(s)) return true; return false;
*/
}
/**
* Determine if a char
is a legal IDL identifier character.
*/
private static boolean isLegalIDLIdentifierChar(char c) {
if (c >= 0x61 && c <= 0x7a)
return true; // lower case letter
if (c >= 0x30 && c <= 0x39)
return true; // digit
if (c >= 0x41 && c <= 0x5a)
return true; // upper case letter
if (c == '_')
return true; // underscore
return false;
}
/**
* Return the class hash code, as specified in "The Common Object Request
* Broker: Architecture and Specification" (01-02-33), section 10.6.2.
*/
static long getClassHashCode(Class cls) {
// The simple cases
if (cls.isInterface())
return 0;
if (!Serializable.class.isAssignableFrom(cls))
return 0;
if (Externalizable.class.isAssignableFrom(cls))
return 1;
// Try cache
Long l = (Long)classHashCodeCache.get(cls);
if (l != null)
return l.longValue();
// Has to calculate the hash.
ByteArrayOutputStream baos = new ByteArrayOutputStream(256);
DataOutputStream dos = new DataOutputStream(baos);
// Step 1
Class superClass = cls.getSuperclass();
if (superClass != null && superClass != Object.class) {
try {
dos.writeLong(getClassHashCode(superClass));
} catch (IOException ex) {
throw new RuntimeException("Unexpected IOException: " + ex);
}
}
// Step 2
boolean hasWriteObject = false;
try {
Method m;
int mods;
m = cls.getDeclaredMethod("writeObject", new Class[] {ObjectOutputStream.class});
mods = m.getModifiers();
if (!Modifier.isPrivate(mods) && !Modifier.isStatic(mods))
hasWriteObject = true;
} catch (NoSuchMethodException ex) {
// ignore
}
try {
dos.writeInt(hasWriteObject ? 2 : 1);
} catch (IOException ex) {
throw new RuntimeException("Unexpected IOException: " + ex);
}
// Step 3
Field[] fields = cls.getDeclaredFields();
SortedSet set = new TreeSet(new FieldComparator());
for (int i = 0; i < fields.length; ++i) {
int mods = fields[i].getModifiers();
if (!Modifier.isStatic(mods) && !Modifier.isTransient(mods))
set.add(fields[i]);
}
Iterator iter = set.iterator();
try {
while (iter.hasNext()) {
Field f = (Field)iter.next();
dos.writeUTF(f.getName());
dos.writeUTF(getSignature(f.getType()));
}
} catch (IOException ex) {
throw new RuntimeException("Unexpected IOException: " + ex);
}
// Convert to byte[]
try {
dos.flush();
} catch (IOException ex) {
throw new RuntimeException("Unexpected IOException: " + ex);
}
byte[] bytes = baos.toByteArray();
// Calculate SHA digest
MessageDigest digest;
try {
digest = MessageDigest.getInstance("SHA");
} catch (NoSuchAlgorithmException ex) {
throw new RuntimeException("No SHA MEssageDigest: " + ex);
}
digest.update(bytes);
byte[] sha = digest.digest();
// Calculate hash as per section 10.6.2
long hash = 0;
for (int i = 0; i < Math.min(8, sha.length); i++) {
hash += (long)(sha[i] & 255) << (i * 8);
}
// Save in cache
classHashCodeCache.put(cls, new Long(hash));
return hash;
}
/**
* Calculate the signature of a class, according to the Java VM
* specification, section 4.3.2.
*/
private static String getSignature(Class cls) {
if (cls.isArray())
return "[" + cls.getComponentType();
if (cls.isPrimitive()) {
if (cls == Byte.TYPE)
return "B";
if (cls == Character.TYPE)
return "C";
if (cls == Double.TYPE)
return "D";
if (cls == Float.TYPE)
return "F";
if (cls == Integer.TYPE)
return "I";
if (cls == Long.TYPE)
return "J";
if (cls == Short.TYPE)
return "S";
if (cls == Boolean.TYPE)
return "Z";
throw new RuntimeException("Unknown primitive class.");
}
return "L" + cls.getName().replace('.', '/') + ";";
}
/**
* Handle mappings for primitive types, as per section 1.3.3.
*/
static String primitiveTypeIDLName(Class type) {
if (type == Void.TYPE)
return "void";
if (type == Boolean.TYPE)
return "boolean";
if (type == Character.TYPE)
return "wchar";
if (type == Byte.TYPE)
return "octet";
if (type == Short.TYPE)
return "short";
if (type == Integer.TYPE)
return "long";
if (type == Long.TYPE)
return "long long";
if (type == Float.TYPE)
return "float";
if (type == Double.TYPE)
return "double";
throw new IllegalArgumentException(type + "is not a primitive type.");
}
/**
* A Comparator
for Field
s, ordering the
* fields according to the lexicographic ordering of their Java names.
*/
private static class FieldComparator implements Comparator {
public int compare(Object o1, Object o2) {
if (o1 == o2)
return 0;
String n1 = ((Field)o1).getName();
String n2 = ((Field)o2).getName();
return n1.compareTo(n2);
}
}
}