ValueType.java revision 608:7e06bf1dcb09
1/*
2 * Copyright (c) 1998, 2007, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.  Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25/*
26 * Licensed Materials - Property of IBM
27 * RMI-IIOP v1.0
28 * Copyright IBM Corp. 1998 1999  All Rights Reserved
29 *
30 */
31
32package sun.rmi.rmic.iiop;
33
34import java.util.Vector;
35import sun.tools.java.ClassNotFound;
36import sun.tools.java.ClassDeclaration;
37import sun.tools.java.ClassDefinition;
38import sun.tools.java.MemberDefinition;
39import java.util.Hashtable;
40import java.io.ObjectStreamClass;
41import java.io.ObjectStreamField;
42
43
44/**
45 * ValueType represents any non-special class which does inherit from
46 * java.io.Serializable and does not inherit from java.rmi.Remote.
47 * <p>
48 * The static forValue(...) method must be used to obtain an instance, and
49 * will return null if the ClassDefinition is non-conforming.
50 *
51 * @author      Bryan Atsatt
52 */
53public class ValueType extends ClassType {
54
55    private boolean isCustom;
56
57    //_____________________________________________________________________
58    // Public Interfaces
59    //_____________________________________________________________________
60
61    /**
62     * Create an ValueType object for the given class.
63     *
64     * If the class is not a properly formed or if some other error occurs, the
65     * return value will be null, and errors will have been reported to the
66     * supplied BatchEnvironment.
67     */
68    public static ValueType forValue(ClassDefinition classDef,
69                                     ContextStack stack,
70                                     boolean quiet) {
71
72        if (stack.anyErrors()) return null;
73
74        // Do we already have it?
75
76        sun.tools.java.Type theType = classDef.getType();
77        String typeKey = theType.toString();
78        Type existing = getType(typeKey,stack);
79
80        if (existing != null) {
81
82            if (!(existing instanceof ValueType)) return null; // False hit.
83
84            // Yep, so return it...
85
86            return (ValueType) existing;
87        }
88
89        // Is this java.lang.Class?
90
91        boolean javaLangClass = false;
92
93        if (classDef.getClassDeclaration().getName() == idJavaLangClass) {
94
95            // Yes, so replace classDef with one for
96            // javax.rmi.CORBA.ClassDesc...
97
98            javaLangClass = true;
99            BatchEnvironment env = stack.getEnv();
100            ClassDeclaration decl = env.getClassDeclaration(idClassDesc);
101            ClassDefinition def = null;
102
103            try {
104                def = decl.getClassDefinition(env);
105            } catch (ClassNotFound ex) {
106                classNotFound(stack,ex);
107                return null;
108            }
109
110            classDef = def;
111        }
112
113        // Could this be a value?
114
115        if (couldBeValue(stack,classDef)) {
116
117            // Yes, so check it...
118
119            ValueType it = new ValueType(classDef,stack,javaLangClass);
120            putType(typeKey,it,stack);
121            stack.push(it);
122
123            if (it.initialize(stack,quiet)) {
124                stack.pop(true);
125                return it;
126            } else {
127                removeType(typeKey,stack);
128                stack.pop(false);
129                return null;
130            }
131        } else {
132            return null;
133        }
134    }
135
136
137    /**
138     * Return a string describing this type.
139     */
140    public String getTypeDescription () {
141        String result = addExceptionDescription("Value");
142        if (isCustom) {
143            result = "Custom " + result;
144        }
145        if (isIDLEntity) {
146            result = result + " [IDLEntity]";
147        }
148        return result;
149    }
150
151    /**
152     * Return true if this type is a "custom" type (i.e.
153     * it implements java.io.Externalizable or has a
154     * method with the following signature:
155     *
156     *  private void writeObject(java.io.ObjectOutputStream out);
157     *
158     */
159    public boolean isCustom () {
160        return isCustom;
161    }
162
163
164    //_____________________________________________________________________
165    // Subclass/Internal Interfaces
166    //_____________________________________________________________________
167
168    /**
169     * Create a ValueType instance for the given class.  The resulting
170     * object is not yet completely initialized.
171     */
172    private ValueType(ClassDefinition classDef,
173                      ContextStack stack,
174                      boolean isMappedJavaLangClass) {
175        super(stack,classDef,TYPE_VALUE | TM_CLASS | TM_COMPOUND);
176        isCustom = false;
177
178        // If this is the mapped version of java.lang.Class,
179        // set the non-IDL names back to java.lang.Class...
180
181        if (isMappedJavaLangClass) {
182            setNames(idJavaLangClass,IDL_CLASS_MODULE,IDL_CLASS);
183        }
184    }
185
186    //_____________________________________________________________________
187    // Internal Interfaces
188    //_____________________________________________________________________
189
190    /**
191     * Initialize this instance.
192     */
193
194    private static boolean couldBeValue(ContextStack stack, ClassDefinition classDef) {
195
196        boolean result = false;
197        ClassDeclaration classDecl = classDef.getClassDeclaration();
198        BatchEnvironment env = stack.getEnv();
199
200        try {
201            // Make sure it's not remote...
202
203            if (env.defRemote.implementedBy(env, classDecl)) {
204                failedConstraint(10,false,stack,classDef.getName());
205            } else {
206
207                // Make sure it's Serializable...
208
209                if (!env.defSerializable.implementedBy(env, classDecl)) {
210                    failedConstraint(11,false,stack,classDef.getName());
211                } else {
212                    result = true;
213                }
214            }
215        } catch (ClassNotFound e) {
216            classNotFound(stack,e);
217        }
218
219        return result;
220    }
221
222    /**
223     * Initialize this instance.
224     */
225    private boolean initialize (ContextStack stack, boolean quiet) {
226
227        ClassDefinition ourDef = getClassDefinition();
228        ClassDeclaration ourDecl = getClassDeclaration();
229
230        try {
231
232            // Make sure our parentage is ok...
233
234            if (!initParents(stack)) {
235                failedConstraint(12,quiet,stack,getQualifiedName());
236                return false;
237            }
238
239
240            // We're ok, so make up our collections...
241
242            Vector directInterfaces = new Vector();
243            Vector directMethods = new Vector();
244            Vector directMembers = new Vector();
245
246            // Get interfaces...
247
248            if (addNonRemoteInterfaces(directInterfaces,stack) != null) {
249
250                // Get methods...
251
252                if (addAllMethods(ourDef,directMethods,false,false,stack) != null) {
253
254                    // Update parent class methods
255                    if (updateParentClassMethods(ourDef,directMethods,false,stack) != null) {
256
257                    // Get constants and members...
258
259                    if (addAllMembers(directMembers,false,false,stack)) {
260
261                        // We're ok, so pass 'em up...
262
263                        if (!initialize(directInterfaces,directMethods,directMembers,stack,quiet)) {
264                            return false;
265                        }
266
267                        // Is this class Externalizable?
268
269                        boolean externalizable = false;
270                        if (!env.defExternalizable.implementedBy(env, ourDecl)) {
271
272                            // No, so check to see if we have a serialPersistentField
273                            // that will modify the members.
274
275                            if (!checkPersistentFields(getClassInstance(),quiet)) {
276                                return false;
277                            }
278                        } else {
279
280                            // Yes.
281
282                            externalizable = true;
283                        }
284
285                        // Should this class be considered "custom"? It is if
286                        // it is Externalizable OR if it has a method with the
287                        // following signature:
288                        //
289                        //  private void writeObject(java.io.ObjectOutputStream out);
290                        //
291
292                        if (externalizable) {
293                            isCustom = true;
294                        } else {
295                            for (MemberDefinition member = ourDef.getFirstMember();
296                                 member != null;
297                                 member = member.getNextMember()) {
298
299                                if (member.isMethod() &&
300                                    !member.isInitializer() &&
301                                    member.isPrivate() &&
302                                    member.getName().toString().equals("writeObject")) {
303
304                                    // Check return type, arguments and exceptions...
305
306                                    sun.tools.java.Type methodType = member.getType();
307                                    sun.tools.java.Type rtnType = methodType.getReturnType();
308
309                                    if (rtnType == sun.tools.java.Type.tVoid) {
310
311                                        // Return type is correct. How about arguments?
312
313                                        sun.tools.java.Type[] args = methodType.getArgumentTypes();
314                                        if (args.length == 1 &&
315                                            args[0].getTypeSignature().equals("Ljava/io/ObjectOutputStream;")) {
316
317                                            // Arguments are correct, so it is a custom
318                                            // value type...
319
320                                            isCustom = true;
321                                        }
322                                    }
323                                }
324                            }
325                        }
326                        }
327
328                        return true;
329                    }
330                }
331            }
332        } catch (ClassNotFound e) {
333            classNotFound(stack,e);
334        }
335
336        return false;
337    }
338
339
340    private boolean checkPersistentFields (Class clz, boolean quiet) {
341
342        // Do we have a writeObject method?
343
344        for (int i = 0; i < methods.length; i++) {
345            if (methods[i].getName().equals("writeObject") &&
346                methods[i].getArguments().length == 1) {
347
348                Type returnType = methods[i].getReturnType();
349                Type arg = methods[i].getArguments()[0];
350                String id = arg.getQualifiedName();
351
352                if (returnType.isType(TYPE_VOID) &&
353                    id.equals("java.io.ObjectOutputStream")) {
354
355                    // Got one, so there's nothing to do...
356
357                    return true;
358                }
359            }
360        }
361
362        // Do we have a valid serialPersistentField array?
363
364        MemberDefinition spfDef = null;
365
366        for (int i = 0; i < members.length; i++) {
367            if (members[i].getName().equals("serialPersistentFields")) {
368
369                Member member = members[i];
370                Type type = member.getType();
371                Type elementType = type.getElementType();
372
373                // We have a member with the correct name. Make sure
374                // we have the correct signature...
375
376                if (elementType != null &&
377                    elementType.getQualifiedName().equals(
378                                                          "java.io.ObjectStreamField")
379                    ) {
380
381                    if (member.isStatic() &&
382                        member.isFinal() &&
383                        member.isPrivate()) {
384
385                        // We have the correct signature
386
387                        spfDef = member.getMemberDefinition();
388
389                    } else {
390
391                        // Bad signature...
392
393                        failedConstraint(4,quiet,stack,getQualifiedName());
394                        return false;
395                    }
396                }
397            }
398        }
399
400        // If we do not have a serialPersistentField,
401        // there's nothing to do, so return with no error...
402
403        if (spfDef == null) {
404            return true;
405        }
406
407        // Ok, now we must examine the contents of the array -
408        // then validate them...
409
410        Hashtable fields = getPersistentFields(clz);
411        boolean result = true;
412
413        for (int i = 0; i < members.length; i++) {
414            String fieldName = members[i].getName();
415            String fieldType = members[i].getType().getSignature();
416
417            // Is this field present in the array?
418
419            String type = (String) fields.get(fieldName);
420
421            if (type == null) {
422
423                // No, so mark it transient...
424
425                members[i].setTransient();
426
427            } else {
428
429                // Yes, does the type match?
430
431                if (type.equals(fieldType)) {
432
433                    // Yes, so remove it from the fields table...
434
435                    fields.remove(fieldName);
436
437                } else {
438
439                    // No, so error...
440
441                    result = false;
442                    failedConstraint(2,quiet,stack,fieldName,getQualifiedName());
443                }
444            }
445        }
446
447        // Ok, we've checked all of our fields. Are there any left in the "array"?
448        // If so, it's an error...
449
450        if (result && fields.size() > 0) {
451
452            result = false;
453            failedConstraint(9,quiet,stack,getQualifiedName());
454        }
455
456        // Return result...
457
458        return result;
459    }
460
461    /**
462     * Get the names and types of all the persistent fields of a Class.
463     */
464    private Hashtable getPersistentFields (Class clz) {
465        Hashtable result = new Hashtable();
466        ObjectStreamClass osc = ObjectStreamClass.lookup(clz);
467        if (osc != null) {
468            ObjectStreamField[] fields = osc.getFields();
469            for (int i = 0; i < fields.length; i++) {
470                String typeSig;
471                String typePrefix = String.valueOf(fields[i].getTypeCode());
472                if (fields[i].isPrimitive()) {
473                    typeSig = typePrefix;
474                } else {
475                    if (fields[i].getTypeCode() == '[') {
476                        typePrefix = "";
477                    }
478                    typeSig = typePrefix + fields[i].getType().getName().replace('.','/');
479                    if (typeSig.endsWith(";")) {
480                        typeSig = typeSig.substring(0,typeSig.length()-1);
481                    }
482                }
483                result.put(fields[i].getName(),typeSig);
484            }
485        }
486        return result;
487    }
488}
489