KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > google > gwt > core > ext > typeinfo > JClassType


1 /*
2  * Copyright 2007 Google Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
5  * use this file except in compliance with the License. You may obtain a copy of
6  * the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13  * License for the specific language governing permissions and limitations under
14  * the License.
15  */

16 package com.google.gwt.core.ext.typeinfo;
17
18 import com.google.gwt.core.ext.UnableToCompleteException;
19
20 import java.io.UnsupportedEncodingException JavaDoc;
21 import java.security.MessageDigest JavaDoc;
22 import java.security.NoSuchAlgorithmException JavaDoc;
23 import java.util.ArrayList JavaDoc;
24 import java.util.Collection JavaDoc;
25 import java.util.HashMap JavaDoc;
26 import java.util.HashSet JavaDoc;
27 import java.util.Iterator JavaDoc;
28 import java.util.List JavaDoc;
29 import java.util.Map JavaDoc;
30 import java.util.Set JavaDoc;
31 import java.util.TreeMap JavaDoc;
32
33 /**
34  * Type representing a Java class or interface type.
35  */

36 public class JClassType extends JType implements HasMetaData {
37   private static final char[] HEX_CHARS = new char[] {
38       '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D',
39       'E', 'F'};
40
41   /**
42    * Computes the MD5 hash for the specified byte array.
43    *
44    * @return a big fat string encoding of the MD5 for the content, suitably
45    * formatted for use as a file name
46    */

47   private static String JavaDoc computeStrongName(byte[] content) {
48     MessageDigest JavaDoc md5;
49     try {
50       md5 = MessageDigest.getInstance("MD5");
51     } catch (NoSuchAlgorithmException JavaDoc e) {
52       throw new RuntimeException JavaDoc("Error initializing MD5", e);
53     }
54     for (int i = 0; i < content.length; i++) {
55       md5.update(content[i]);
56     }
57     byte[] hash = md5.digest();
58     // Hex version of the hash.
59
//
60
char[] name = new char[2 * hash.length];
61     int j = 0;
62     for (int i = 0; i < hash.length; i++) {
63       name[j++] = HEX_CHARS[(hash[i] & 0xF0) >> 4];
64       name[j++] = HEX_CHARS[hash[i] & 0x0F];
65     }
66     return new String JavaDoc(name);
67   }
68
69   private final Set JavaDoc allSubtypes = new HashSet JavaDoc();
70
71   private final int bodyEnd;
72
73   private final int bodyStart;
74
75   private JMethod[] cachedOverridableMethods;
76
77   private final List JavaDoc constructors = new ArrayList JavaDoc();
78
79   private final CompilationUnitProvider cup;
80
81   private final JPackage declaringPackage;
82
83   private final int declEnd;
84
85   private final int declStart;
86
87   private final JClassType enclosingType;
88
89   private final Map JavaDoc fields = new HashMap JavaDoc();
90
91   private final List JavaDoc interfaces = new ArrayList JavaDoc();
92
93   private final boolean isInterface;
94
95   private final boolean isLocalType;
96
97   private String JavaDoc lazyHash;
98
99   private String JavaDoc lazyQualifiedName;
100
101   private final HasMetaData metaData = new MetaData();
102
103   private final Map JavaDoc methods = new HashMap JavaDoc();
104
105   private int modifierBits;
106
107   private final String JavaDoc name;
108
109   private final String JavaDoc nestedName;
110
111   private final Map JavaDoc nestedTypes = new HashMap JavaDoc();
112
113   private final TypeOracle oracle;
114
115   private JClassType superclass;
116
117   public JClassType(TypeOracle oracle, CompilationUnitProvider cup,
118       JPackage declaringPackage, JClassType enclosingType, boolean isLocalType,
119       String JavaDoc name, int declStart, int declEnd, int bodyStart, int bodyEnd,
120       boolean isInterface) {
121     oracle.recordTypeInCompilationUnit(cup, this);
122     this.oracle = oracle;
123     this.cup = cup;
124     this.declaringPackage = declaringPackage;
125     this.enclosingType = enclosingType;
126     this.isLocalType = isLocalType;
127     this.name = name;
128     this.declStart = declStart;
129     this.declEnd = declEnd;
130     this.bodyStart = bodyStart;
131     this.bodyEnd = bodyEnd;
132     this.isInterface = isInterface;
133     if (enclosingType == null) {
134       // Add myself to my package.
135
//
136
declaringPackage.addType(this);
137       // The nested name of a top-level class is its simple name.
138
//
139
nestedName = name;
140     } else {
141       // Add myself to my enclosing type.
142
//
143
enclosingType.addNestedType(this);
144       // Compute my "nested name".
145
//
146
JClassType enclosing = enclosingType;
147       String JavaDoc nn = name;
148       do {
149         nn = enclosing.getSimpleSourceName() + "." + nn;
150         enclosing = enclosing.getEnclosingType();
151       } while (enclosing != null);
152       nestedName = nn;
153     }
154   }
155
156   public void addImplementedInterface(JClassType intf) {
157     assert (intf != null);
158     interfaces.add(intf);
159   }
160
161   public void addMetaData(String JavaDoc tagName, String JavaDoc[] values) {
162     metaData.addMetaData(tagName, values);
163   }
164
165   public void addModifierBits(int bits) {
166     modifierBits |= bits;
167   }
168
169   public JConstructor findConstructor(JType[] paramTypes) {
170     JConstructor[] ctors = getConstructors();
171     for (int i = 0; i < ctors.length; i++) {
172       JConstructor candidate = ctors[i];
173       if (candidate.hasParamTypes(paramTypes)) {
174         return candidate;
175       }
176     }
177     return null;
178   }
179
180   public JField findField(String JavaDoc name) {
181     return (JField) fields.get(name);
182   }
183
184   public JMethod findMethod(String JavaDoc name, JType[] paramTypes) {
185     JMethod[] overloads = getOverloads(name);
186     for (int i = 0; i < overloads.length; i++) {
187       JMethod candidate = overloads[i];
188       if (candidate.hasParamTypes(paramTypes)) {
189         return candidate;
190       }
191     }
192     return null;
193   }
194
195   public JClassType findNestedType(String JavaDoc typeName) {
196     String JavaDoc[] parts = typeName.split("\\.");
197     return findNestedTypeImpl(parts, 0);
198   }
199
200   public int getBodyEnd() {
201     return bodyEnd;
202   }
203
204   public int getBodyStart() {
205     return bodyStart;
206   }
207
208   public CompilationUnitProvider getCompilationUnit() {
209     return cup;
210   }
211
212   public JConstructor getConstructor(JType[] paramTypes)
213       throws NotFoundException {
214     JConstructor result = findConstructor(paramTypes);
215     if (result == null) {
216       throw new NotFoundException();
217     }
218     return result;
219   }
220
221   public JConstructor[] getConstructors() {
222     return (JConstructor[]) constructors.toArray(TypeOracle.NO_JCTORS);
223   }
224
225   public JClassType getEnclosingType() {
226     return enclosingType;
227   }
228
229   public JField getField(String JavaDoc name) {
230     JField field = findField(name);
231     assert (field != null);
232     return field;
233   }
234
235   public JField[] getFields() {
236     return (JField[]) fields.values().toArray(TypeOracle.NO_JFIELDS);
237   }
238
239   public JClassType[] getImplementedInterfaces() {
240     return (JClassType[]) interfaces.toArray(TypeOracle.NO_JCLASSES);
241   }
242
243   public String JavaDoc getJNISignature() {
244     String JavaDoc typeName = nestedName.replace('.', '$');
245     String JavaDoc packageName = getPackage().getName().replace('.', '/');
246     if (packageName.length() > 0) {
247       packageName += "/";
248     }
249     return "L" + packageName + typeName + ";";
250   }
251
252   public String JavaDoc[][] getMetaData(String JavaDoc tagName) {
253     return metaData.getMetaData(tagName);
254   }
255
256   public String JavaDoc[] getMetaDataTags() {
257     return metaData.getMetaDataTags();
258   }
259
260   public JMethod getMethod(String JavaDoc name, JType[] paramTypes)
261       throws NotFoundException {
262     JMethod result = findMethod(name, paramTypes);
263     if (result == null) {
264       throw new NotFoundException();
265     }
266     return result;
267   }
268
269   /*
270    * Returns the declared methods of this class (not any superclasses or
271    * superinterfaces).
272    */

273   public JMethod[] getMethods() {
274     List JavaDoc resultMethods = new ArrayList JavaDoc();
275     for (Iterator JavaDoc iter = methods.values().iterator(); iter.hasNext();) {
276       List JavaDoc overloads = (List JavaDoc) iter.next();
277       resultMethods.addAll(overloads);
278     }
279     return (JMethod[]) resultMethods.toArray(TypeOracle.NO_JMETHODS);
280   }
281
282   public String JavaDoc getName() {
283     return nestedName;
284   }
285
286   public JClassType getNestedType(String JavaDoc typeName) throws NotFoundException {
287     JClassType result = findNestedType(typeName);
288     if (result == null) {
289       throw new NotFoundException();
290     }
291     return result;
292   }
293
294   public JClassType[] getNestedTypes() {
295     return (JClassType[]) nestedTypes.values().toArray(TypeOracle.NO_JCLASSES);
296   }
297
298   public TypeOracle getOracle() {
299     return oracle;
300   }
301
302   public JMethod[] getOverloads(String JavaDoc name) {
303     List JavaDoc resultMethods = (List JavaDoc) methods.get(name);
304     if (resultMethods != null) {
305       return (JMethod[]) resultMethods.toArray(TypeOracle.NO_JMETHODS);
306     } else {
307       return TypeOracle.NO_JMETHODS;
308     }
309   }
310
311   /**
312    * Iterates over the most-derived declaration of each unique overridable
313    * method available in the type hierarchy of the specified type, including
314    * those found in superclasses and superinterfaces. A method is overridable if
315    * it is not <code>final</code> and its accessibility is <code>public</code>,
316    * <code>protected</code>, or package protected.
317    *
318    * Deferred binding generators often need to generate method implementations;
319    * this method offers a convenient way to find candidate methods to implement.
320    *
321    * Note that the behavior does not match
322    * {@link Class#getMethod(String, Class[])}, which does not return the most
323    * derived method in some cases.
324    *
325    * @return an array of {@link JMethod} objects representing overridable
326    * methods
327    */

328   public JMethod[] getOverridableMethods() {
329     if (cachedOverridableMethods == null) {
330       Map JavaDoc methodsBySignature = new TreeMap JavaDoc();
331       getOverridableMethodsOnSuperinterfacesAndMaybeThisInterface(methodsBySignature);
332       if (isClass() != null) {
333         getOverridableMethodsOnSuperclassesAndThisClass(methodsBySignature);
334       }
335       int size = methodsBySignature.size();
336       Collection JavaDoc leafMethods = methodsBySignature.values();
337       cachedOverridableMethods = (JMethod[]) leafMethods.toArray(new JMethod[size]);
338     }
339     return cachedOverridableMethods;
340   }
341
342   public JPackage getPackage() {
343     return declaringPackage;
344   }
345
346   public String JavaDoc getQualifiedSourceName() {
347     if (lazyQualifiedName == null) {
348       JPackage pkg = getPackage();
349       if (!pkg.isDefault()) {
350         lazyQualifiedName = pkg.getName() + "." + makeCompoundName(this);
351       } else {
352         lazyQualifiedName = makeCompoundName(this);
353       }
354     }
355     return lazyQualifiedName;
356   }
357
358   public String JavaDoc getSimpleSourceName() {
359     return name;
360   }
361
362   public JClassType[] getSubtypes() {
363     return (JClassType[]) allSubtypes.toArray(TypeOracle.NO_JCLASSES);
364   }
365
366   public JClassType getSuperclass() {
367     return superclass;
368   }
369
370   public String JavaDoc getTypeHash() throws UnableToCompleteException {
371     if (lazyHash == null) {
372       char[] source = cup.getSource();
373       int length = declEnd - declStart + 1;
374       String JavaDoc s = new String JavaDoc(source, declStart, length);
375       try {
376         lazyHash = computeStrongName(s.getBytes("UTF-8"));
377       } catch (UnsupportedEncodingException JavaDoc e) {
378         // Details, details...
379
throw new UnableToCompleteException();
380       }
381     }
382     return lazyHash;
383   }
384
385   public boolean isAbstract() {
386     return 0 != (modifierBits & TypeOracle.MOD_ABSTRACT);
387   }
388
389   public JArrayType isArray() {
390     // intentional null
391
return null;
392   }
393
394   public boolean isAssignableFrom(JClassType possibleSubtype) {
395     if (possibleSubtype == this) {
396       return true;
397     }
398     if (allSubtypes.contains(possibleSubtype)) {
399       return true;
400     } else if (this == getOracle().getJavaLangObject()) {
401       // This case handles the odd "every interface is an Object"
402
// but doesn't actually have Object as a superclass.
403
//
404
return true;
405     } else {
406       return false;
407     }
408   }
409
410   public boolean isAssignableTo(JClassType possibleSupertype) {
411     return possibleSupertype.isAssignableFrom(this);
412   }
413
414   public JClassType isClass() {
415     return isInterface ? null : this;
416   }
417
418   /**
419    * Determines if the class can be constructed using a simple <code>new</code>
420    * operation. Specifically, the class must
421    * <ul>
422    * <li>be a class rather than an interface, </li>
423    * <li>have either no constructors or a parameterless constructor, and</li>
424    * <li>be a top-level class or a static nested class.</li>
425    * </ul>
426    *
427    * @return <code>true</code> if the type is default instantiable, or
428    * <code>false</code> otherwise
429    */

430   public boolean isDefaultInstantiable() {
431     if (isInterface() != null) {
432       return false;
433     }
434     if (constructors.isEmpty()) {
435       return true;
436     }
437     JConstructor ctor = findConstructor(TypeOracle.NO_JTYPES);
438     if (ctor != null) {
439       return true;
440     }
441     return false;
442   }
443
444   public JClassType isInterface() {
445     return isInterface ? this : null;
446   }
447
448   /**
449    * Tests if this type is a local type (within a method).
450    *
451    * @return true if this type is a local type, whether it is named or
452    * anonymous.
453    */

454   public boolean isLocalType() {
455     return isLocalType;
456   }
457
458   /**
459    * Tests if this type is contained within another type.
460    *
461    * @return true if this type has an enclosing type, false if this type is a
462    * top-level type
463    */

464   public boolean isMemberType() {
465     return enclosingType != null;
466   }
467
468   public JParameterizedType isParameterized() {
469     // intentional null
470
return null;
471   }
472
473   public JPrimitiveType isPrimitive() {
474     // intentional null
475
return null;
476   }
477
478   public boolean isPrivate() {
479     return 0 != (modifierBits & TypeOracle.MOD_PRIVATE);
480   }
481
482   public boolean isProtected() {
483     return 0 != (modifierBits & TypeOracle.MOD_PROTECTED);
484   }
485
486   public boolean isPublic() {
487     return 0 != (modifierBits & TypeOracle.MOD_PUBLIC);
488   }
489
490   public boolean isStatic() {
491     return 0 != (modifierBits & TypeOracle.MOD_STATIC);
492   }
493
494   public void setSuperclass(JClassType type) {
495     assert (type != null);
496     assert (isInterface() == null);
497     this.superclass = type;
498   }
499
500   public String JavaDoc toString() {
501     if (isInterface) {
502       return "interface " + getQualifiedSourceName();
503     } else {
504       return "class " + getQualifiedSourceName();
505     }
506   }
507
508   protected int getModifierBits() {
509     return modifierBits;
510   }
511
512   void addConstructor(JConstructor ctor) {
513     assert (!constructors.contains(ctor));
514     constructors.add(ctor);
515   }
516
517   void addField(JField field) {
518     Object JavaDoc existing = fields.put(field.getName(), field);
519     assert (existing == null);
520   }
521
522   void addMethod(JMethod method) {
523     String JavaDoc methodName = method.getName();
524     List JavaDoc overloads = (List JavaDoc) methods.get(methodName);
525     if (overloads == null) {
526       overloads = new ArrayList JavaDoc();
527       methods.put(methodName, overloads);
528     }
529     overloads.add(method);
530   }
531
532   void addNestedType(JClassType type) {
533     Object JavaDoc existing = nestedTypes.put(type.getSimpleSourceName(), type);
534   }
535
536   JClassType findNestedTypeImpl(String JavaDoc[] typeName, int index) {
537     JClassType found = (JClassType) nestedTypes.get(typeName[index]);
538     if (found == null) {
539       return null;
540     } else if (index < typeName.length - 1) {
541       return found.findNestedTypeImpl(typeName, index + 1);
542     } else {
543       return found;
544     }
545   }
546
547   void notifySuperTypes() {
548     notifySuperTypesOf(this);
549   }
550
551   /**
552    * Removes references to this instance from all of its super types.
553    */

554   void removeFromSupertypes() {
555     removeSubtype(this);
556   }
557
558   private void acceptSubtype(JClassType me) {
559     allSubtypes.add(me);
560     notifySuperTypesOf(me);
561   }
562
563   private String JavaDoc computeInternalSignature(JMethod method) {
564     StringBuffer JavaDoc sb = new StringBuffer JavaDoc();
565     sb.setLength(0);
566     sb.append(method.getName());
567     JParameter[] params = method.getParameters();
568     for (int j = 0; j < params.length; j++) {
569       JParameter param = params[j];
570       sb.append("/");
571       sb.append(param.getType().getQualifiedSourceName());
572     }
573     return sb.toString();
574   }
575
576   private void getOverridableMethodsOnSuperclassesAndThisClass(
577       Map JavaDoc methodsBySignature) {
578     assert (isClass() != null);
579
580     // Recurse first so that more derived methods will clobber less derived
581
// methods.
582
JClassType superClass = getSuperclass();
583     if (superClass != null) {
584       superClass.getOverridableMethodsOnSuperclassesAndThisClass(methodsBySignature);
585     }
586
587     JMethod[] declaredMethods = getMethods();
588     for (int i = 0; i < declaredMethods.length; i++) {
589       JMethod method = declaredMethods[i];
590
591       // Ensure that this method is overridable.
592
if (method.isFinal() || method.isPrivate()) {
593         // We cannot override this method, so skip it.
594
continue;
595       }
596
597       // We can override this method, so record it.
598
String JavaDoc sig = computeInternalSignature(method);
599       methodsBySignature.put(sig, method);
600     }
601   }
602
603   /**
604    * Gets the methods declared in interfaces that this type extends. If this
605    * type is a class, its own methods are not added. If this type is an
606    * interface, its own methods are added. Used internally by
607    * {@link #getOverridableMethods()}.
608    *
609    * @param methodsBySignature
610    */

611   private void getOverridableMethodsOnSuperinterfacesAndMaybeThisInterface(
612       Map JavaDoc methodsBySignature) {
613     // Recurse first so that more derived methods will clobber less derived
614
// methods.
615
JClassType[] superIntfs = getImplementedInterfaces();
616     for (int i = 0; i < superIntfs.length; i++) {
617       JClassType superIntf = superIntfs[i];
618       superIntf.getOverridableMethodsOnSuperinterfacesAndMaybeThisInterface(methodsBySignature);
619     }
620
621     if (isInterface() == null) {
622       // This is not an interface, so we're done after having visited its
623
// implemented interfaces.
624
return;
625     }
626
627     JMethod[] declaredMethods = getMethods();
628     for (int i = 0; i < declaredMethods.length; i++) {
629       JMethod method = declaredMethods[i];
630
631       String JavaDoc sig = computeInternalSignature(method);
632       JMethod existing = (JMethod) methodsBySignature.get(sig);
633       if (existing != null) {
634         JClassType existingType = existing.getEnclosingType();
635         JClassType thisType = method.getEnclosingType();
636         if (thisType.isAssignableFrom(existingType)) {
637           // The existing method is in a more-derived type, so don't replace it.
638
continue;
639         }
640       }
641       methodsBySignature.put(sig, method);
642     }
643   }
644
645   private String JavaDoc makeCompoundName(JClassType type) {
646     if (type.enclosingType == null) {
647       return type.name;
648     } else {
649       return makeCompoundName(type.enclosingType) + "." + type.name;
650     }
651   }
652
653   /**
654    * Tells this type's superclasses and superinterfaces about it.
655    */

656   private void notifySuperTypesOf(JClassType me) {
657     if (superclass != null) {
658       superclass.acceptSubtype(me);
659     }
660     for (int i = 0, n = interfaces.size(); i < n; ++i) {
661       JClassType intf = (JClassType) interfaces.get(i);
662       intf.acceptSubtype(me);
663     }
664   }
665
666   private void removeSubtype(JClassType me) {
667     allSubtypes.remove(me);
668
669     if (superclass != null) {
670       superclass.removeSubtype(me);
671     }
672
673     for (int i = 0, n = interfaces.size(); i < n; ++i) {
674       JClassType intf = (JClassType) interfaces.get(i);
675
676       intf.removeSubtype(me);
677     }
678   }
679 }
680
Popular Tags