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 }