package org.jboss.iiop.rmi;
import org.omg.CORBA.ORB;
import org.omg.CORBA.TCKind;
import org.omg.CORBA.TypeCode;
import org.omg.CORBA.AttributeMode;
import java.lang.reflect.Method;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.ObjectOutputStream;
import java.io.ObjectStreamClass;
import java.io.Serializable;
import java.io.Externalizable;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Map;
import java.util.WeakHashMap;
import java.util.SortedSet;
import java.util.TreeSet;
import java.util.Iterator;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
public abstract class ContainerAnalysis
extends ClassAnalysis
{
protected final byte M_OVERLOADED = 1;
protected final byte M_READ = 2;
protected final byte M_WRITE = 4;
protected final byte M_READONLY = 8;
protected final byte M_INHERITED = 16;
protected final byte M_WRITEOBJECT = 32;
protected final byte F_CONSTANT = 1;
protected final byte F_SPFFIELD = 2;
protected Method[] methods;
protected byte[] m_flags;
protected int[] mutators;
protected Field[] fields;
protected byte[] f_flags;
protected long classHashCode = 0;
protected String repositoryId;
protected String memberPrefix, memberPostfix;
protected InterfaceAnalysis[] interfaces;
protected ValueAnalysis[] abstractBaseValuetypes;
protected AttributeAnalysis[] attributes;
protected ConstantAnalysis[] constants;
protected OperationAnalysis[] operations;
private static final org.jboss.logging.Logger logger =
org.jboss.logging.Logger.getLogger(ContainerAnalysis.class);
protected ContainerAnalysis(Class cls)
{
super(cls);
if (cls == java.lang.Object.class ||
cls == java.io.Serializable.class ||
cls == java.io.Externalizable.class)
throw new IllegalArgumentException("Cannot analyze special class: " +
cls.getName());
this.cls = cls;
}
protected void doAnalyze()
throws RMIIIOPViolationException
{
analyzeInterfaces();
analyzeMethods();
analyzeFields();
calculateClassHashCode();
calculateRepositoryId();
analyzeAttributes();
analyzeConstants();
analyzeOperations();
fixupOverloadedOperationNames();
}
public InterfaceAnalysis[] getInterfaces()
{
logger.debug(cls + " Interface count: " + interfaces.length);
return (InterfaceAnalysis[])interfaces.clone();
}
public ValueAnalysis[] getAbstractBaseValuetypes()
{
logger.debug(cls + " Abstract base valuetype count: " + abstractBaseValuetypes.length);
return (ValueAnalysis[])abstractBaseValuetypes.clone();
}
public AttributeAnalysis[] getAttributes()
{
logger.debug(cls + " Attribute count: " + attributes.length);
return (AttributeAnalysis[])attributes.clone();
}
public ConstantAnalysis[] getConstants()
{
logger.debug(cls + " Constants count: " + constants.length);
return (ConstantAnalysis[])constants.clone();
}
public OperationAnalysis[] getOperations()
{
logger.debug(cls + " Operations count: " + operations.length);
return (OperationAnalysis[])operations.clone();
}
public String getRepositoryId()
{
return repositoryId;
}
public String getMemberRepositoryId(String memberName)
{
return memberPrefix + escapeIRName(memberName) + memberPostfix;
}
public String getIDLModuleName()
{
if (idlModuleName == null) {
String pkgName = cls.getPackage().getName();
StringBuffer b = new StringBuffer();
while (!"".equals(pkgName)) {
int idx = pkgName.indexOf('.');
String n = (idx == -1) ? pkgName : pkgName.substring(0, idx);
b.append("::").append(Util.javaToIDLName(n));
pkgName = (idx == -1) ? "" : pkgName.substring(idx+1);
}
idlModuleName = b.toString();
}
return idlModuleName;
}
protected String toHexString(int i)
{
String s = Integer.toHexString(i).toUpperCase();
if (s.length() < 8)
return "00000000".substring(0, 8 - s.length()) + s;
else
return s;
}
protected String toHexString(long l)
{
String s = Long.toHexString(l).toUpperCase();
if (s.length() < 16)
return "0000000000000000".substring(0, 16 - s.length()) + s;
else
return s;
}
protected boolean isAccessor(Method m)
{
Class returnType = m.getReturnType();
if (!m.getName().startsWith("get"))
if (!m.getName().startsWith("is") || !(returnType == Boolean.TYPE))
return false;
if (returnType == Void.TYPE)
return false;
if (m.getParameterTypes().length != 0)
return false;
return hasNonAppExceptions(m);
}
protected boolean isMutator(Method m)
{
if (!m.getName().startsWith("set"))
return false;
if (m.getReturnType() != Void.TYPE)
return false;
if (m.getParameterTypes().length != 1)
return false;
return hasNonAppExceptions(m);
}
protected boolean hasNonAppExceptions(Method m)
{
Class[] ex = m.getExceptionTypes();
for (int i = 0; i < ex.length; ++i)
if (!java.rmi.RemoteException.class.isAssignableFrom(ex[i]))
return false;
return true;
}
protected void analyzeFields()
{
logger.debug(cls + " analyzeFields");
fields = cls.getDeclaredFields();
f_flags = new byte[fields.length];
for (int i = 0; i < fields.length; ++i) {
int mods = fields[i].getModifiers();
if (Modifier.isFinal(mods) &&
Modifier.isStatic(mods) &&
Modifier.isPublic(mods))
f_flags[i] |= F_CONSTANT;
}
logger.debug(cls + " analyzeFields fields=" + fields.length);
}
protected void analyzeInterfaces()
throws RMIIIOPViolationException
{
logger.debug(cls + " analyzeInterfaces");
Class[] intfs = cls.getInterfaces();
ArrayList a = new ArrayList();
ArrayList b = new ArrayList();
for (int i = 0; i < intfs.length; ++i) {
if (intfs[i] == java.rmi.Remote.class)
continue;
if (intfs[i] == java.io.Serializable.class)
continue;
if (intfs[i] == java.io.Externalizable.class)
continue;
if (!RmiIdlUtil.isAbstractValueType(intfs[i])) {
a.add(InterfaceAnalysis.getInterfaceAnalysis(intfs[i]));
}
else {
b.add(ValueAnalysis.getValueAnalysis(intfs[i]));
}
}
interfaces = new InterfaceAnalysis[a.size()];
interfaces = (InterfaceAnalysis[])a.toArray(interfaces);
abstractBaseValuetypes = new ValueAnalysis[b.size()];
abstractBaseValuetypes = (ValueAnalysis[])b.toArray(abstractBaseValuetypes);
logger.debug(cls + " analyzeInterfaces interfaces=" + interfaces.length + " abstractBaseValueTypes=" + abstractBaseValuetypes.length);
}
protected void analyzeMethods()
{
logger.debug(cls + " analyzeMethods");
if (cls.isInterface() && java.rmi.Remote.class.isAssignableFrom(cls))
methods = cls.getMethods();
else
methods = cls.getDeclaredMethods();
m_flags = new byte[methods.length];
mutators = new int[methods.length];
for (int i = 0; i < methods.length; ++i)
mutators[i] = -1; for (int i = 0; i < methods.length; ++i) {
logger.debug("analyzeMethods(): method["+i+"].getName()=\"" +
methods[i].getName() + "\".");
if (isAccessor(methods[i]) && (m_flags[i]&M_READ) == 0) {
String attrName = attributeReadName(methods[i].getName());
Class iReturn = methods[i].getReturnType();
for (int j = i+1; j < methods.length; ++j) {
if (isMutator(methods[j]) && (m_flags[j]&M_WRITE) == 0 &&
attrName.equals(attributeWriteName(methods[j].getName()))) {
Class[] jParams = methods[j].getParameterTypes();
if (jParams.length == 1 && jParams[0] == iReturn) {
m_flags[i] |= M_READ;
m_flags[j] |= M_WRITE;
mutators[i] = j;
break;
}
}
}
} else if (isMutator(methods[i]) && (m_flags[i]&M_WRITE) == 0) {
String attrName = attributeWriteName(methods[i].getName());
Class[] iParams = methods[i].getParameterTypes();
for (int j = i+1; j < methods.length; ++j) {
if (isAccessor(methods[j]) && (m_flags[j]&M_READ) == 0 &&
attrName.equals(attributeReadName(methods[j].getName()))) {
Class jReturn = methods[j].getReturnType();
if (iParams.length == 1 && iParams[0] == jReturn) {
m_flags[i] |= M_WRITE;
m_flags[j] |= M_READ;
mutators[j] = i;
break;
}
}
}
}
}
for (int i = 0; i < methods.length; ++i)
if ((m_flags[i] & (M_READ|M_WRITE)) == 0 && isAccessor(methods[i]))
m_flags[i] |= M_READONLY;
for (int i = 0; i < methods.length; ++i) {
if ((m_flags[i] & (M_READ|M_WRITE|M_READONLY)) == 0) {
String iName = methods[i].getName();
for (int j = i+1; j < methods.length; ++j) {
if (iName.equals(methods[j].getName())) {
m_flags[i] |= M_OVERLOADED;
m_flags[j] |= M_OVERLOADED;
}
}
}
if (methods[i].getDeclaringClass() != cls)
m_flags[i] |= M_INHERITED;
}
logger.debug(cls + " analyzeMethods methods=" + methods.length);
}
protected String attributeReadName(String name)
{
if (name.startsWith("get"))
name = name.substring(3);
else if (name.startsWith("is"))
name = name.substring(2);
else
throw new IllegalArgumentException("Not an accessor: " + name);
return name;
}
protected String attributeWriteName(String name)
{
if (name.startsWith("set"))
name = name.substring(3);
else
throw new IllegalArgumentException("Not an accessor: " + name);
return name;
}
protected void analyzeConstants()
throws RMIIIOPViolationException
{
logger.debug(cls + " analyzeConstants");
ArrayList a = new ArrayList();
for (int i = 0; i < fields.length; ++i) {
logger.debug("f_flags["+i+"]=" + f_flags[i]);
if ((f_flags[i] & F_CONSTANT) == 0)
continue;
Class type = fields[i].getType();
if (!type.isPrimitive() && type != java.lang.String.class) {
if (cls.isInterface())
throw new RMIIIOPViolationException(
"Field \"" + fields[i].getName() + "\" of interface \"" +
cls.getName() + "\" is a constant, but not of one " +
"of the primitive types, or String.", "1.2.3");
continue;
}
String name = fields[i].getName();
Object value;
try {
value = fields[i].get(null);
} catch (Exception ex) {
throw new RuntimeException(ex.toString());
}
logger.debug("Constant["+i+"] name= " + name);
logger.debug("Constant["+i+"] type= " + type.getName());
logger.debug("Constant["+i+"] value= " + value);
a.add(new ConstantAnalysis(name, type, value));
}
constants = new ConstantAnalysis[a.size()];
constants = (ConstantAnalysis[])a.toArray(constants);
logger.debug(cls + " analyzeConstants constant=" + a.size());
}
protected void analyzeAttributes()
throws RMIIIOPViolationException
{
logger.debug(cls + " analyzeAttributes");
ArrayList a = new ArrayList();
for (int i = 0; i < methods.length; ++i) {
logger.debug("m_flags["+i+"]=" + m_flags[i]);
if ((m_flags[i] & (M_READ|M_READONLY)) != 0) {
String name = attributeReadName(methods[i].getName());
logger.debug("Attribute["+i+"] name= " + name);
if ((m_flags[i]&M_READONLY) != 0)
a.add(new AttributeAnalysis(name, methods[i]));
else
a.add(new AttributeAnalysis(name, methods[i],
methods[mutators[i]]));
}
}
attributes = new AttributeAnalysis[a.size()];
attributes = (AttributeAnalysis[])a.toArray(attributes);
logger.debug(cls + " analyzeAttributes attributes=" + a.size());
}
protected void analyzeOperations()
throws RMIIIOPViolationException
{
logger.debug(cls + " analyzeOperations");
operations = new OperationAnalysis[0];
logger.debug(cls + " analyzeOperations operations=" + operations.length);
}
protected void fixupOverloadedOperationNames()
throws RMIIIOPViolationException
{
for (int i = 0; i < methods.length; ++i) {
if ((m_flags[i]&M_OVERLOADED) == 0)
continue;
OperationAnalysis oa = null;
String javaName = methods[i].getName();
for (int opIdx = 0; oa == null && opIdx < operations.length; ++opIdx)
if (operations[opIdx].getMethod().equals(methods[i]))
oa = operations[opIdx];
if (oa == null)
continue;
ParameterAnalysis[] parms = oa.getParameters();
StringBuffer b = new StringBuffer(oa.getIDLName());
if (parms.length == 0)
b.append("__");
for (int j = 0; j < parms.length; ++j) {
String s = parms[j].getTypeIDLName();
if (s.startsWith("::"))
s = s.substring(2);
if (s.startsWith("_")) {
s = s.substring(1);
}
b.append('_');
while (!"".equals(s)) {
int idx = s.indexOf("::");
b.append('_');
if (idx == -1) {
b.append(s);
s = "";
} else {
b.append(s.substring(0, idx));
if (s.length() > idx + 2 && s.charAt(idx + 2) == '_') {
s = s.substring(idx + 3);
} else {
s = s.substring(idx + 2);
}
}
}
}
oa.setIDLName(b.toString());
}
}
protected void fixupCaseNames()
throws RMIIIOPViolationException
{
ArrayList entries = getContainedEntries();
boolean[] clash = new boolean[entries.size()];
String[] upperNames = new String[entries.size()];
for (int i = 0; i < entries.size(); ++i) {
AbstractAnalysis aa = (AbstractAnalysis)entries.get(i);
clash[i] = false;
upperNames[i] = aa.getIDLName().toUpperCase();
for (int j = 0; j < i; ++j) {
if (upperNames[i].equals(upperNames[j])) {
clash[i] = true;
clash[j] = true;
}
}
}
for (int i = 0; i < entries.size(); ++i) {
if (!clash[i])
continue;
AbstractAnalysis aa = (AbstractAnalysis)entries.get(i);
boolean noUpper = true;
String name = aa.getIDLName();
StringBuffer b = new StringBuffer(name);
b.append('_');
for (int j = 0; j < name.length(); ++j) {
if (!Character.isUpperCase(name.charAt(j)))
continue;
if (noUpper)
noUpper = false;
else
b.append('_');
b.append(j);
}
aa.setIDLName(b.toString());
}
}
abstract protected ArrayList getContainedEntries();
protected void calculateClassHashCode()
{
if (cls.isInterface())
classHashCode = 0;
else if (!Serializable.class.isAssignableFrom(cls))
classHashCode = 0;
else if (Externalizable.class.isAssignableFrom(cls))
classHashCode = 1;
else classHashCode = Util.getClassHashCode(cls);
}
protected String escapeIRName(String name)
{
StringBuffer b = new StringBuffer();
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));
}
return b.toString();
}
protected void calculateRepositoryId()
{
if (cls.isArray() || cls.isPrimitive())
throw new IllegalArgumentException("Not a class or interface.");
if (cls.isInterface() &&
org.omg.CORBA.Object.class.isAssignableFrom(cls) &&
org.omg.CORBA.portable.IDLEntity.class.isAssignableFrom(cls)) {
StringBuffer b = new StringBuffer("IDL:");
b.append(cls.getPackage().getName().replace('.', '/'));
b.append('/');
String base = cls.getName();
base = base.substring(base.lastIndexOf('.')+1);
b.append(base).append(":1.0");
repositoryId = b.toString();
}
else {
StringBuffer b = new StringBuffer("RMI:");
b.append(escapeIRName(cls.getName()));
memberPrefix = b.toString() + ".";
String hashStr = toHexString(classHashCode);
b.append(':').append(hashStr);
ObjectStreamClass osClass = ObjectStreamClass.lookup(cls);
if (osClass != null) {
long serialVersionUID = osClass.getSerialVersionUID();
String SVUID = toHexString(serialVersionUID);
if (classHashCode != serialVersionUID)
b.append(':').append(SVUID);
memberPostfix = ":" + hashStr + ":" + SVUID;
} else
memberPostfix = ":" + hashStr;
repositoryId = b.toString();
}
}
private String idlModuleName = null;
}