001    /*
002     * Copyright 2006 Google Inc.
003     * 
004     * Licensed under the Apache License, Version 2.0 (the "License"); you may not
005     * use this file except in compliance with the License. You may obtain a copy of
006     * the License at
007     * 
008     * http://www.apache.org/licenses/LICENSE-2.0
009     * 
010     * Unless required by applicable law or agreed to in writing, software
011     * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
012     * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
013     * License for the specific language governing permissions and limitations under
014     * the License.
015     */
016    package com.google.gwt.core.ext.typeinfo;
017    
018    import com.google.gwt.core.ext.UnableToCompleteException;
019    
020    import java.io.UnsupportedEncodingException;
021    import java.security.MessageDigest;
022    import java.security.NoSuchAlgorithmException;
023    import java.util.ArrayList;
024    import java.util.HashMap;
025    import java.util.HashSet;
026    import java.util.Iterator;
027    import java.util.List;
028    import java.util.Map;
029    import java.util.Set;
030    
031    public class JClassType extends JType implements HasMetaData {
032    
033      private static final char[] HEX_CHARS = new char[]{
034        '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E',
035        'F'};
036    
037      /**
038       * Computes the MD5 hash for the specified byte array.
039       * 
040       * @return a big fat string encoding of the MD5 for the content, suitably
041       *         formatted for use as a file name
042       */
043      private static String computeStrongName(byte[] content)
044          throws UnableToCompleteException {
045        MessageDigest md5;
046        try {
047          md5 = MessageDigest.getInstance("MD5");
048        } catch (NoSuchAlgorithmException e) {
049          throw new RuntimeException("Error initializing MD5", e);
050        }
051    
052        for (int i = 0; i < content.length; i++) {
053          md5.update(content[i]);
054        }
055    
056        byte[] hash = md5.digest();
057    
058        // Hex version of the hash.
059        //        
060        char[] name = new char[2 * hash.length];
061        int j = 0;
062        for (int i = 0; i < hash.length; i++) {
063          name[j++] = HEX_CHARS[(hash[i] & 0xF0) >> 4];
064          name[j++] = HEX_CHARS[hash[i] & 0x0F];
065        }
066    
067        return new String(name);
068      }
069    
070      public JClassType(TypeOracle oracle, CompilationUnitProvider cup,
071          JPackage declaringPackage, JClassType enclosingType, String name,
072          int declStart, int declEnd, int bodyStart, int bodyEnd,
073          boolean isInterface) {
074        oracle.recordTypeInCompilationUnit(cup, this);
075        this.oracle = oracle;
076        this.cup = cup;
077        this.declaringPackage = declaringPackage;
078        this.enclosingType = enclosingType;
079        this.name = name;
080        this.declStart = declStart;
081        this.declEnd = declEnd;
082        this.bodyStart = bodyStart;
083        this.bodyEnd = bodyEnd;
084        this.isInterface = isInterface;
085    
086        if (enclosingType == null) {
087          // Add myself to my package.
088          //
089          declaringPackage.addType(this);
090    
091          // The nested name of a top-level class is its simple name.
092          //
093          nestedName = name;
094        } else {
095          // Add myself to my enclosing type.
096          //
097          enclosingType.addNestedType(this);
098    
099          // Compute my "nested name".
100          //
101          JClassType enclosing = enclosingType;
102          String nn = name;
103          do {
104            nn = enclosing.getSimpleSourceName() + "." + nn;
105            enclosing = enclosing.getEnclosingType();
106          } while (enclosing != null);
107          nestedName = nn;
108        }
109      }
110    
111      public void addImplementedInterface(JClassType intf) {
112        assert (intf != null);
113        interfaces.add(intf);
114      }
115    
116      public void addMetaData(String tagName, String[] values) {
117        metaData.addMetaData(tagName, values);
118      }
119    
120      public void addModifierBits(int bits) {
121        modifierBits |= bits;
122      }
123    
124      public JConstructor findConstructor(JType[] paramTypes) {
125        JConstructor[] ctors = getConstructors();
126        for (int i = 0; i < ctors.length; i++) {
127          JConstructor candidate = ctors[i];
128          if (candidate.hasParamTypes(paramTypes))
129            return candidate;
130        }
131        return null;
132      }
133    
134      public JField findField(String name) {
135        return (JField) fields.get(name);
136      }
137    
138      public JMethod findMethod(String name, JType[] paramTypes) {
139        JMethod[] overloads = getOverloads(name);
140        for (int i = 0; i < overloads.length; i++) {
141          JMethod candidate = overloads[i];
142          if (candidate.hasParamTypes(paramTypes))
143            return candidate;
144        }
145        return null;
146      }
147    
148      public JClassType findNestedType(String typeName) {
149        String[] parts = typeName.split("\\.");
150        return findNestedTypeImpl(parts, 0);
151      }
152    
153      public int getBodyEnd() {
154        return bodyEnd;
155      }
156    
157      public int getBodyStart() {
158        return bodyStart;
159      }
160    
161      public CompilationUnitProvider getCompilationUnit() {
162        return cup;
163      }
164    
165      public JConstructor getConstructor(JType[] paramTypes)
166          throws NotFoundException {
167        JConstructor result = findConstructor(paramTypes);
168        if (result == null) {
169          throw new NotFoundException();
170        }
171        return result;
172      }
173    
174      public JConstructor[] getConstructors() {
175        return (JConstructor[]) constructors.toArray(TypeOracle.NO_JCTORS);
176      }
177    
178      public JClassType getEnclosingType() {
179        return enclosingType;
180      }
181    
182      public JField getField(String name) {
183        JField field = findField(name);
184        assert (field != null);
185        return field;
186      }
187    
188      public JField[] getFields() {
189        return (JField[]) fields.values().toArray(TypeOracle.NO_JFIELDS);
190      }
191    
192      public JClassType[] getImplementedInterfaces() {
193        return (JClassType[]) interfaces.toArray(TypeOracle.NO_JCLASSES);
194      }
195    
196      public String getJNISignature() {
197        String typeName = nestedName.replace('.', '$');
198        String packageName = getPackage().getName().replace('.', '/');
199        if (packageName.length() > 0) {
200          packageName += "/";
201        }
202        return "L" + packageName + typeName + ";";
203      }
204    
205      public String[][] getMetaData(String tagName) {
206        return metaData.getMetaData(tagName);
207      }
208    
209      public String[] getMetaDataTags() {
210        return metaData.getMetaDataTags();
211      }
212    
213      public JMethod getMethod(String name, JType[] paramTypes)
214          throws NotFoundException {
215        JMethod result = findMethod(name, paramTypes);
216        if (result == null) {
217          throw new NotFoundException();
218        }
219        return result;
220      }
221    
222      public JMethod[] getMethods() {
223        List resultMethods = new ArrayList();
224        for (Iterator iter = methods.values().iterator(); iter.hasNext();) {
225          List overloads = (List) iter.next();
226          resultMethods.addAll(overloads);
227        }
228        return (JMethod[]) resultMethods.toArray(TypeOracle.NO_JMETHODS);
229      }
230    
231      public String getName() {
232        return nestedName;
233      }
234    
235      public JClassType getNestedType(String typeName) throws NotFoundException {
236        JClassType result = findNestedType(typeName);
237        if (result == null) {
238          throw new NotFoundException();
239        }
240        return result;
241      }
242    
243      public JClassType[] getNestedTypes() {
244        return (JClassType[]) nestedTypes.values().toArray(TypeOracle.NO_JCLASSES);
245      }
246    
247      public TypeOracle getOracle() {
248        return oracle;
249      }
250    
251      public JMethod[] getOverloads(String name) {
252        List resultMethods = (List) methods.get(name);
253        if (resultMethods != null) {
254          return (JMethod[]) resultMethods.toArray(TypeOracle.NO_JMETHODS);
255        } else {
256          return TypeOracle.NO_JMETHODS;
257        }
258      }
259    
260      public JPackage getPackage() {
261        return declaringPackage;
262      }
263    
264      public String getQualifiedSourceName() {
265        if (lazyQualifiedName == null) {
266          JPackage pkg = getPackage();
267          if (!pkg.isDefault())
268            lazyQualifiedName = pkg.getName() + "." + makeCompoundName(this);
269          else
270            lazyQualifiedName = makeCompoundName(this);
271        }
272        return lazyQualifiedName;
273      }
274    
275      public String getSimpleSourceName() {
276        return name;
277      }
278    
279      public JClassType[] getSubtypes() {
280        return (JClassType[]) allSubtypes.toArray(TypeOracle.NO_JCLASSES);
281      }
282    
283      public JClassType getSuperclass() {
284        return superclass;
285      }
286    
287      public String getTypeHash() throws UnableToCompleteException {
288        if (lazyHash == null) {
289          char[] source = cup.getSource();
290          int length = declEnd - declStart + 1;
291          String s = new String(source, declStart, length);
292          try {
293            lazyHash = computeStrongName(s.getBytes("UTF-8"));
294          } catch (UnsupportedEncodingException e) {
295            // Details, details...
296            throw new UnableToCompleteException();
297          }
298        }
299        return lazyHash;
300      }
301    
302      public boolean isAbstract() {
303        return 0 != (modifierBits & TypeOracle.MOD_ABSTRACT);
304      }
305    
306      public JArrayType isArray() {
307        // intentional null
308        return null;
309      }
310    
311      public boolean isAssignableTo(JClassType possibleSupertype) {
312        return possibleSupertype.isAssignableFrom(this);
313      }
314    
315      public JClassType isClass() {
316        return isInterface ? null : this;
317      }
318    
319      /**
320       * Determines if the class can be constructed using a simple <code>new</code>
321       * operation. Specifically, the class must
322       * <ul>
323       * <li>be a class rather than an interface, </li>
324       * <li>have either no constructors or a parameterless <code>public</code>
325       * constructor, and</li>
326       * <li>be a top-level class or a static nested class.</li>
327       * </ul>
328       * 
329       * @return <code>true</code> if the type is default instantiable, or
330       *         <code>false</code> otherwise
331       */
332      public boolean isDefaultInstantiable() {
333        if (isInterface() != null) {
334          return false;
335        }
336    
337        if (constructors.isEmpty())
338          return true;
339    
340        JConstructor ctor = findConstructor(TypeOracle.NO_JTYPES);
341        if (ctor != null)
342          return true;
343    
344        return false;
345      }
346    
347      public JClassType isInterface() {
348        return isInterface ? this : null;
349      }
350    
351      public JParameterizedType isParameterized() {
352        // intentional null
353        return null;
354      }
355    
356      public JPrimitiveType isPrimitive() {
357        // intentional null
358        return null;
359      }
360    
361      public boolean isPrivate() {
362        return 0 != (modifierBits & TypeOracle.MOD_PRIVATE);
363      }
364    
365      public boolean isProtected() {
366        return 0 != (modifierBits & TypeOracle.MOD_PROTECTED);
367      }
368    
369      public boolean isPublic() {
370        return 0 != (modifierBits & TypeOracle.MOD_PUBLIC);
371      }
372    
373      public boolean isStatic() {
374        return 0 != (modifierBits & TypeOracle.MOD_STATIC);
375      }
376    
377      public void setSuperclass(JClassType type) {
378        assert (type != null);
379        assert (isInterface() == null);
380        this.superclass = type;
381      }
382    
383      public String toString() {
384        if (isInterface)
385          return "interface " + getQualifiedSourceName();
386        else
387          return "class " + getQualifiedSourceName();
388      }
389    
390      protected int getModifierBits() {
391        return modifierBits;
392      }
393    
394      void addConstructor(JConstructor ctor) {
395        assert (!constructors.contains(ctor));
396        constructors.add(ctor);
397      }
398    
399      void addField(JField field) {
400        Object existing = fields.put(field.getName(), field);
401        assert (existing == null);
402      }
403    
404      void addMethod(JMethod method) {
405        String name = method.getName();
406        List overloads = (List) methods.get(name);
407        if (overloads == null) {
408          overloads = new ArrayList();
409          methods.put(name, overloads);
410        }
411        overloads.add(method);
412      }
413    
414      void addNestedType(JClassType type) {
415        Object existing = nestedTypes.put(type.getSimpleSourceName(), type);
416        assert (existing == null);
417      }
418    
419      JClassType findNestedTypeImpl(String[] typeName, int index) {
420        JClassType found = (JClassType) nestedTypes.get(typeName[index]);
421        if (found == null)
422          return null;
423        else if (index < typeName.length - 1)
424          return found.findNestedTypeImpl(typeName, index + 1);
425        else
426          return found;
427      }
428    
429      void notifySuperTypes() {
430        notifySuperTypesOf(this);
431      }
432    
433      private void acceptSubtype(JClassType me) {
434        allSubtypes.add(me);
435        notifySuperTypesOf(me);
436      }
437    
438      private boolean isAssignableFrom(JClassType possibleSubtype) {
439        if (possibleSubtype == this) {
440          return true;
441        }
442        if (allSubtypes.contains(possibleSubtype)) {
443          return true;
444        } else if (this == getOracle().getJavaLangObject()) {
445          // This case handles the odd "every interface is an Object"
446          // but doesn't actually have Object as a superclass.
447          //
448          return true;
449        } else {
450          return false;
451        }
452      }
453    
454      private String makeCompoundName(JClassType type) {
455        if (type.enclosingType == null)
456          return type.name;
457        else
458          return makeCompoundName(type.enclosingType) + "." + type.name;
459      }
460    
461      /**
462       * Tells this type's superclasses and superinterfaces about it.
463       */
464      private void notifySuperTypesOf(JClassType me) {
465        if (superclass != null) {
466          superclass.acceptSubtype(me);
467        }
468    
469        for (int i = 0, n = interfaces.size(); i < n; ++i) {
470          JClassType intf = (JClassType) interfaces.get(i);
471          intf.acceptSubtype(me);
472        }
473      }
474      private final Set allSubtypes = new HashSet();
475      private final int bodyEnd;
476      private final int bodyStart;
477      private final List constructors = new ArrayList();
478      private final CompilationUnitProvider cup;
479      private final JPackage declaringPackage;
480      private final int declEnd;
481      private final int declStart;
482      private final JClassType enclosingType;
483      private final Map fields = new HashMap();
484      private final List interfaces = new ArrayList();
485      private boolean isInterface;
486      private String lazyHash;
487      private String lazyQualifiedName;
488      private final HasMetaData metaData = new MetaData();
489      private final Map methods = new HashMap();
490      private int modifierBits;
491      private final String name;
492      private final String nestedName;
493      private final Map nestedTypes = new HashMap();
494      private final TypeOracle oracle;
495    
496      private JClassType superclass;
497    }