001: /*
002: * JBoss, Home of Professional Open Source.
003: * Copyright 2006, Red Hat Middleware LLC, and individual contributors
004: * as indicated by the @author tags. See the copyright.txt file in the
005: * distribution for a full listing of individual contributors.
006: *
007: * This is free software; you can redistribute it and/or modify it
008: * under the terms of the GNU Lesser General Public License as
009: * published by the Free Software Foundation; either version 2.1 of
010: * the License, or (at your option) any later version.
011: *
012: * This software is distributed in the hope that it will be useful,
013: * but WITHOUT ANY WARRANTY; without even the implied warranty of
014: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015: * Lesser General Public License for more details.
016: *
017: * You should have received a copy of the GNU Lesser General Public
018: * License along with this software; if not, write to the Free
019: * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
020: * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
021: */
022: package org.jboss.iiop.rmi;
023:
024: import org.omg.CORBA.ORB;
025: import org.omg.CORBA.TCKind;
026: import org.omg.CORBA.TypeCode;
027: import org.omg.CORBA.portable.IDLEntity;
028: import org.omg.CORBA.portable.ValueBase;
029:
030: import java.rmi.Remote;
031:
032: import java.io.Serializable;
033: import java.io.Externalizable;
034: import java.io.IOException;
035: import java.io.ObjectStreamField;
036:
037: import java.util.ArrayList;
038: import java.util.Collections;
039: import java.util.SortedSet;
040: import java.util.TreeSet;
041: import java.util.Comparator;
042: import java.util.Map;
043: import java.util.WeakHashMap;
044:
045: import java.lang.reflect.Method;
046: import java.lang.reflect.Field;
047: import java.lang.reflect.Modifier;
048:
049: /**
050: * Value analysis.
051: *
052: * Routines here are conforming to the "Java(TM) Language to IDL Mapping
053: * Specification", version 1.1 (01-06-07).
054: *
055: * @author <a href="mailto:osh@sparre.dk">Ole Husgaard</a>
056: * @version $Revision: 57194 $
057: */
058: public class ValueAnalysis extends ContainerAnalysis {
059: // Constants -----------------------------------------------------
060:
061: // Attributes ----------------------------------------------------
062:
063: // Static --------------------------------------------------------
064:
065: private static final org.jboss.logging.Logger logger = org.jboss.logging.Logger
066: .getLogger(ValueAnalysis.class);
067:
068: private static WorkCacheManager cache = new WorkCacheManager(
069: ValueAnalysis.class);
070:
071: public static ValueAnalysis getValueAnalysis(Class cls)
072: throws RMIIIOPViolationException {
073: return (ValueAnalysis) cache.getAnalysis(cls);
074: }
075:
076: // Constructors --------------------------------------------------
077:
078: protected ValueAnalysis(Class cls) {
079: super (cls);
080: logger.debug("ValueAnalysis(\"" + cls.getName()
081: + "\") entered.");
082: }
083:
084: public String getIDLModuleName() {
085: String result = super .getIDLModuleName();
086:
087: // Checked for boxedIDL 1.3.9
088: Class clazz = getCls();
089: if (IDLEntity.class.isAssignableFrom(clazz)
090: && ValueBase.class.isAssignableFrom(clazz) == false)
091: result = "::org::omg::boxedIDL" + result;
092: return result;
093: }
094:
095: protected void doAnalyze() throws RMIIIOPViolationException {
096: super .doAnalyze();
097:
098: if (cls == String.class)
099: throw new IllegalArgumentException(
100: "Cannot analyze java.lang.String here: It is a "
101: + "special case."); // 1.3.5.11
102:
103: if (cls == Class.class)
104: throw new IllegalArgumentException(
105: "Cannot analyze java.lang.Class here: It is a "
106: + "special case."); // 1.3.5.10
107:
108: if (Remote.class.isAssignableFrom(cls))
109: throw new RMIIIOPViolationException("Value type "
110: + cls.getName()
111: + " cannot implement java.rmi.Remote.", "1.2.4");
112:
113: if (cls.getName().indexOf('$') != -1)
114: throw new RMIIIOPNotImplementedException("Class "
115: + cls.getName() + " has a '$', like "
116: + "proxies or inner classes.");
117:
118: externalizable = Externalizable.class.isAssignableFrom(cls);
119:
120: if (!externalizable) {
121: // Look for serialPersistentFields field.
122: Field spf = null;
123: try {
124: spf = cls.getField("serialPersistentFields");
125: } catch (NoSuchFieldException ex) {
126: // ignore
127: }
128: if (spf != null) { // Right modifiers?
129: int mods = spf.getModifiers();
130: if (!Modifier.isFinal(mods) || !Modifier.isStatic(mods)
131: || !Modifier.isPrivate(mods))
132: spf = null; // wrong modifiers
133: }
134: if (spf != null) { // Right type?
135: Class type = spf.getType();
136: if (type.isArray()) {
137: type = type.getComponentType();
138: if (type != ObjectStreamField.class)
139: spf = null; // Array of wrong type
140: } else
141: spf = null; // Wrong type: Not an array
142: }
143: if (spf != null) {
144: // We have the serialPersistentFields field
145:
146: // Get this constant
147: try {
148: serialPersistentFields = (ObjectStreamField[]) spf
149: .get(null);
150: } catch (IllegalAccessException ex) {
151: throw new RuntimeException(
152: "Unexpected IllegalException: "
153: + ex.toString());
154: }
155:
156: // Mark this in the fields array
157: for (int i = 0; i < fields.length; ++i) {
158: if (fields[i] == spf) {
159: f_flags[i] |= F_SPFFIELD;
160: break;
161: }
162: }
163: }
164:
165: // Look for a writeObject Method
166: Method wo = null;
167: try {
168: wo = cls.getMethod("writeObject",
169: new Class[] { java.io.OutputStream[].class });
170: } catch (NoSuchMethodException ex) {
171: // ignore
172: }
173: if (wo != null) { // Right return type?
174: if (wo.getReturnType() != Void.TYPE)
175: wo = null; // Wrong return type
176: }
177: if (wo != null) { // Right modifiers?
178: int mods = spf.getModifiers();
179: if (!Modifier.isPrivate(mods))
180: wo = null; // wrong modifiers
181: }
182: if (wo != null) { // Right arguments?
183: Class[] paramTypes = wo.getParameterTypes();
184: if (paramTypes.length != 1)
185: wo = null; // Bad number of parameters
186: else if (paramTypes[0] != java.io.OutputStream.class)
187: wo = null; // Bad parameter type
188: }
189: if (wo != null) {
190: // We have the writeObject() method.
191: hasWriteObjectMethod = true;
192:
193: // Mark this in the methods array
194: for (int i = 0; i < methods.length; ++i) {
195: if (methods[i] == wo) {
196: m_flags[i] |= M_WRITEOBJECT;
197: break;
198: }
199: }
200: }
201: }
202:
203: // Map all fields not flagged constant or serialPersistentField.
204: SortedSet m = new TreeSet(new ValueMemberComparator());
205:
206: logger.debug("ValueAnalysis(\"" + cls.getName() + "\"): "
207: + "fields.length=" + fields.length);
208: for (int i = 0; i < fields.length; ++i) {
209: logger.debug("ValueAnalysis(\"" + cls.getName() + "\"): "
210: + "Considering field[" + i + "] \""
211: + fields[i].getName() + "\"" + " f_flags="
212: + f_flags[i]);
213: if (f_flags[i] != 0)
214: continue; // flagged
215:
216: int mods = fields[i].getModifiers();
217: logger.debug("ValueAnalysis(\"" + cls.getName()
218: + "\"): mods=" + mods);
219: if (Modifier.isStatic(mods) || Modifier.isTransient(mods))
220: continue; // don't map this
221:
222: ValueMemberAnalysis vma;
223: vma = new ValueMemberAnalysis(fields[i].getName(),
224: fields[i].getType(), Modifier.isPublic(mods));
225: m.add(vma);
226: }
227:
228: members = new ValueMemberAnalysis[m.size()];
229: members = (ValueMemberAnalysis[]) m.toArray(members);
230: logger.debug("ValueAnalysis(\"" + cls.getName()
231: + "\") value member count: " + members.length);
232:
233: // Get superclass analysis
234: Class super Class = cls.getSuperclass();
235: if (super Class == java.lang.Object.class)
236: super Class = null;
237: if (super Class == null)
238: super Analysis = null;
239: else {
240: logger.debug("ValueAnalysis(\"" + cls.getName()
241: + "\"): superclass: " + super Class.getName());
242: super Analysis = getValueAnalysis(super Class);
243: }
244:
245: if (!Serializable.class.isAssignableFrom(cls))
246: abstractValue = true;
247:
248: fixupCaseNames();
249:
250: logger.debug("ValueAnalysis(\"" + cls.getName() + "\") done.");
251: }
252:
253: // Public --------------------------------------------------------
254:
255: /**
256: * Returns the superclass analysis, or null if this inherits from
257: * java.lang.Object.
258: */
259: public ValueAnalysis getSuperAnalysis() {
260: return super Analysis;
261: }
262:
263: /**
264: * Returns true if this value is abstract.
265: */
266: public boolean isAbstractValue() {
267: return abstractValue;
268: }
269:
270: /**
271: * Returns true if this value is custom.
272: */
273: public boolean isCustom() {
274: return externalizable || hasWriteObjectMethod;
275: }
276:
277: /**
278: * Returns true if this value implements java.io.Externalizable.
279: */
280: public boolean isExternalizable() {
281: return externalizable;
282: }
283:
284: /**
285: * Return the value members of this value class.
286: */
287: public ValueMemberAnalysis[] getMembers() {
288: return (ValueMemberAnalysis[]) members.clone();
289: }
290:
291: // Protected -----------------------------------------------------
292:
293: /**
294: * Analyse attributes.
295: * This will fill in the <code>attributes</code> array.
296: * Here we override the implementation in ContainerAnalysis and create an
297: * empty array, because for valuetypes we don't want to analyse IDL
298: * attributes or operations (as in "rmic -idl -noValueMethods").
299: */
300: protected void analyzeAttributes() throws RMIIIOPViolationException {
301: attributes = new AttributeAnalysis[0];
302: }
303:
304: /**
305: * Return a list of all the entries contained here.
306: *
307: * @param entries The list of entries contained here. Entries in this list
308: * are subclasses of <code>AbstractAnalysis</code>.
309: */
310: protected ArrayList getContainedEntries() {
311: ArrayList ret = new ArrayList(constants.length
312: + attributes.length + members.length);
313:
314: for (int i = 0; i < constants.length; ++i)
315: ret.add(constants[i]);
316: for (int i = 0; i < attributes.length; ++i)
317: ret.add(attributes[i]);
318: for (int i = 0; i < members.length; ++i)
319: ret.add(members[i]);
320:
321: return ret;
322: }
323:
324: // Private -------------------------------------------------------
325:
326: /**
327: * Analysis of our superclass, of null if our superclass is
328: * java.lang.Object.
329: */
330: ValueAnalysis super Analysis;
331:
332: /**
333: * Flags that this is an abstract value.
334: */
335: private boolean abstractValue = false;
336:
337: /**
338: * Flags that this implements <code>java.io.Externalizable</code>.
339: */
340: private boolean externalizable = false;
341:
342: /**
343: * Flags that this has a <code>writeObject()</code> method.
344: */
345: private boolean hasWriteObjectMethod = false;
346:
347: /**
348: * The <code>serialPersistentFields of the value, or <code>null</code>
349: * if the value does not have this field.
350: */
351: private ObjectStreamField[] serialPersistentFields;
352:
353: /**
354: * The value members of this value class.
355: */
356: private ValueMemberAnalysis[] members;
357:
358: // Inner classes ------------------------------------------------
359:
360: /**
361: * A <code>Comparator</code> for the field ordering specified at the
362: * end of section 1.3.5.6.
363: */
364: private static class ValueMemberComparator implements Comparator {
365: public int compare(Object o1, Object o2) {
366: if (o1 == o2)
367: return 0;
368:
369: ValueMemberAnalysis m1 = (ValueMemberAnalysis) o1;
370: ValueMemberAnalysis m2 = (ValueMemberAnalysis) o2;
371:
372: boolean p1 = m1.getCls().isPrimitive();
373: boolean p2 = m2.getCls().isPrimitive();
374:
375: if (p1 && !p2)
376: return -1;
377: if (!p1 && p2)
378: return 1;
379:
380: return m1.getJavaName().compareTo(m2.getJavaName());
381: }
382: }
383: }
|