/*
* JBoss, Home of Professional Open Source.
* Copyright 2008, Red Hat Middleware LLC, and individual contributors
* as indicated by the @author tags. See the copyright.txt file in the
* distribution for a full listing of individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.wildfly.iiop.openjdk.rmi;
import java.io.Externalizable;
import java.io.ObjectStreamField;
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.util.ArrayList;
import java.util.Comparator;
import java.util.SortedSet;
import java.util.TreeSet;
import org.omg.CORBA.portable.IDLEntity;
import org.omg.CORBA.portable.ValueBase;
import org.wildfly.iiop.openjdk.logging.IIOPLogger;
/**
* Value analysis.
* <p/>
* Routines here are conforming to the "Java(TM) Language to IDL Mapping
* Specification", version 1.1 (01-06-07).
*
* @author <a href="mailto:osh@sparre.dk">Ole Husgaard</a>
*/
public class ValueAnalysis extends ContainerAnalysis {
private static WorkCacheManager cache = new WorkCacheManager(ValueAnalysis.class);
/**
* Analysis of our superclass, of null if our superclass is
* java.lang.Object.
*/
private ValueAnalysis superAnalysis;
/**
* Flags that this is an abstract value.
*/
private boolean abstractValue = false;
/**
* Flags that this implements <code>java.io.Externalizable</code>.
*/
private boolean externalizable = false;
/**
* Flags that this has a <code>writeObject()</code> method.
*/
private boolean hasWriteObjectMethod = false;
/**
* The <code>serialPersistentFields of the value, or <code>null</code>
* if the value does not have this field.
*/
private ObjectStreamField[] serialPersistentFields;
/**
* The value members of this value class.
*/
private ValueMemberAnalysis[] members;
public static ValueAnalysis getValueAnalysis(Class cls) throws RMIIIOPViolationException {
return (ValueAnalysis) cache.getAnalysis(cls);
}
public static void clearCache(final ClassLoader classLoader) {
cache.clearClassLoader(classLoader);
}
protected ValueAnalysis(final Class cls) {
super(cls);
}
public String getIDLModuleName() {
String result = super.getIDLModuleName();
// Checked for boxedIDL 1.3.9
Class clazz = getCls();
if (IDLEntity.class.isAssignableFrom(clazz) && ValueBase.class.isAssignableFrom(clazz) == false)
result = "::org::omg::boxedIDL" + result;
return result;
}
protected void doAnalyze() throws RMIIIOPViolationException {
super.doAnalyze();
if (cls == String.class)
throw IIOPLogger.ROOT_LOGGER.cannotAnalyzeStringType();
if (cls == Class.class)
throw IIOPLogger.ROOT_LOGGER.cannotAnalyzeClassType();
if (Remote.class.isAssignableFrom(cls))
throw IIOPLogger.ROOT_LOGGER.valueTypeCantImplementRemote(cls.getName(), "1.2.4");
if (cls.getName().indexOf('$') != -1)
throw IIOPLogger.ROOT_LOGGER.valueTypeCantBeProxy(cls.getName());
externalizable = Externalizable.class.isAssignableFrom(cls);
if (!externalizable) {
// Look for serialPersistentFields field.
Field spf = null;
try {
spf = cls.getField("serialPersistentFields");
} catch (NoSuchFieldException ex) {
// ignore
}
if (spf != null) { // Right modifiers?
int mods = spf.getModifiers();
if (!Modifier.isFinal(mods) || !Modifier.isStatic(mods) ||
!Modifier.isPrivate(mods))
spf = null; // wrong modifiers
}
if (spf != null) { // Right type?
Class type = spf.getType();
if (type.isArray()) {
type = type.getComponentType();
if (type != ObjectStreamField.class)
spf = null; // Array of wrong type
} else
spf = null; // Wrong type: Not an array
}
if (spf != null) {
// We have the serialPersistentFields field
// Get this constant
try {
serialPersistentFields = (ObjectStreamField[]) spf.get(null);
} catch (IllegalAccessException ex) {
throw IIOPLogger.ROOT_LOGGER.unexpectedException(ex);
}
// Mark this in the fields array
for (int i = 0; i < fields.length; ++i) {
if (fields[i] == spf) {
f_flags[i] |= F_SPFFIELD;
break;
}
}
}
// Look for a writeObject Method
Method wo = null;
try {
wo = cls.getMethod("writeObject", new Class[]{java.io.OutputStream[].class});
} catch (NoSuchMethodException ex) {
// ignore
}
if (wo != null) { // Right return type?
if (wo.getReturnType() != Void.TYPE)
wo = null; // Wrong return type
}
if (wo != null) { // Right modifiers?
int mods = spf.getModifiers();
if (!Modifier.isPrivate(mods))
wo = null; // wrong modifiers
}
if (wo != null) { // Right arguments?
Class[] paramTypes = wo.getParameterTypes();
if (paramTypes.length != 1)
wo = null; // Bad number of parameters
else if (paramTypes[0] != java.io.OutputStream.class)
wo = null; // Bad parameter type
}
if (wo != null) {
// We have the writeObject() method.
hasWriteObjectMethod = true;
// Mark this in the methods array
for (int i = 0; i < methods.length; ++i) {
if (methods[i] == wo) {
m_flags[i] |= M_WRITEOBJECT;
break;
}
}
}
}
// Map all fields not flagged constant or serialPersistentField.
SortedSet m = new TreeSet(new ValueMemberComparator());
for (int i = 0; i < fields.length; ++i) {
if (f_flags[i] != 0)
continue; // flagged
int mods = fields[i].getModifiers();
if (Modifier.isStatic(mods) || Modifier.isTransient(mods))
continue; // don't map this
ValueMemberAnalysis vma;
vma = new ValueMemberAnalysis(fields[i].getName(),
fields[i].getType(),
Modifier.isPublic(mods));
m.add(vma);
}
members = new ValueMemberAnalysis[m.size()];
members = (ValueMemberAnalysis[]) m.toArray(members);
// Get superclass analysis
Class superClass = cls.getSuperclass();
if (superClass == java.lang.Object.class)
superClass = null;
if (superClass == null)
superAnalysis = null;
else {
superAnalysis = getValueAnalysis(superClass);
}
if (!Serializable.class.isAssignableFrom(cls))
abstractValue = true;
fixupCaseNames();
}
// Public --------------------------------------------------------
/**
* Returns the superclass analysis, or null if this inherits from
* java.lang.Object.
*/
public ValueAnalysis getSuperAnalysis() {
return superAnalysis;
}
/**
* Returns true if this value is abstract.
*/
public boolean isAbstractValue() {
return abstractValue;
}
/**
* Returns true if this value is custom.
*/
public boolean isCustom() {
return externalizable || hasWriteObjectMethod;
}
/**
* Returns true if this value implements java.io.Externalizable.
*/
public boolean isExternalizable() {
return externalizable;
}
/**
* Return the value members of this value class.
*/
public ValueMemberAnalysis[] getMembers() {
return members.clone();
}
/**
* Analyse attributes.
* This will fill in the <code>attributes</code> array.
* Here we override the implementation in ContainerAnalysis and create an
* empty array, because for valuetypes we don't want to analyse IDL
* attributes or operations (as in "rmic -idl -noValueMethods").
*/
protected void analyzeAttributes() throws RMIIIOPViolationException {
attributes = new AttributeAnalysis[0];
}
/**
* Return a list of all the entries contained here.
*
*/
protected ArrayList getContainedEntries() {
final ArrayList ret = new ArrayList(constants.length +
attributes.length +
members.length);
for (int i = 0; i < constants.length; ++i)
ret.add(constants[i]);
for (int i = 0; i < attributes.length; ++i)
ret.add(attributes[i]);
for (int i = 0; i < members.length; ++i)
ret.add(members[i]);
return ret;
}
/**
* A <code>Comparator</code> for the field ordering specified at the
* end of section 1.3.5.6.
*/
private static class ValueMemberComparator implements Comparator {
public int compare(final Object o1, final Object o2) {
if (o1 == o2)
return 0;
final ValueMemberAnalysis m1 = (ValueMemberAnalysis) o1;
final ValueMemberAnalysis m2 = (ValueMemberAnalysis) o2;
final boolean p1 = m1.getCls().isPrimitive();
final boolean p2 = m2.getCls().isPrimitive();
if (p1 && !p2)
return -1;
if (!p1 && p2)
return 1;
return m1.getJavaName().compareTo(m2.getJavaName());
}
}
}