0001: /*
0002: * Copyright 2007 Google Inc.
0003: *
0004: * Licensed under the Apache License, Version 2.0 (the "License"); you may not
0005: * use this file except in compliance with the License. You may obtain a copy of
0006: * the License at
0007: *
0008: * http://www.apache.org/licenses/LICENSE-2.0
0009: *
0010: * Unless required by applicable law or agreed to in writing, software
0011: * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
0012: * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
0013: * License for the specific language governing permissions and limitations under
0014: * the License.
0015: */
0016: package com.google.gwt.dev.jdt;
0017:
0018: import com.google.gwt.core.ext.TreeLogger;
0019: import com.google.gwt.core.ext.UnableToCompleteException;
0020: import com.google.gwt.core.ext.typeinfo.CompilationUnitProvider;
0021: import com.google.gwt.core.ext.typeinfo.HasMetaData;
0022: import com.google.gwt.core.ext.typeinfo.JAbstractMethod;
0023: import com.google.gwt.core.ext.typeinfo.JAnnotationMethod;
0024: import com.google.gwt.core.ext.typeinfo.JAnnotationType;
0025: import com.google.gwt.core.ext.typeinfo.JArrayType;
0026: import com.google.gwt.core.ext.typeinfo.JBound;
0027: import com.google.gwt.core.ext.typeinfo.JClassType;
0028: import com.google.gwt.core.ext.typeinfo.JConstructor;
0029: import com.google.gwt.core.ext.typeinfo.JEnumConstant;
0030: import com.google.gwt.core.ext.typeinfo.JEnumType;
0031: import com.google.gwt.core.ext.typeinfo.JField;
0032: import com.google.gwt.core.ext.typeinfo.JGenericType;
0033: import com.google.gwt.core.ext.typeinfo.JLowerBound;
0034: import com.google.gwt.core.ext.typeinfo.JMethod;
0035: import com.google.gwt.core.ext.typeinfo.JPackage;
0036: import com.google.gwt.core.ext.typeinfo.JParameter;
0037: import com.google.gwt.core.ext.typeinfo.JParameterizedType;
0038: import com.google.gwt.core.ext.typeinfo.JPrimitiveType;
0039: import com.google.gwt.core.ext.typeinfo.JRealClassType;
0040: import com.google.gwt.core.ext.typeinfo.JType;
0041: import com.google.gwt.core.ext.typeinfo.JTypeParameter;
0042: import com.google.gwt.core.ext.typeinfo.JUpperBound;
0043: import com.google.gwt.core.ext.typeinfo.TypeOracle;
0044: import com.google.gwt.dev.jdt.CacheManager.Mapper;
0045: import com.google.gwt.dev.util.Empty;
0046: import com.google.gwt.dev.util.Util;
0047:
0048: import org.eclipse.jdt.core.compiler.CharOperation;
0049: import org.eclipse.jdt.core.compiler.IProblem;
0050: import org.eclipse.jdt.internal.compiler.ASTVisitor;
0051: import org.eclipse.jdt.internal.compiler.CompilationResult;
0052: import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration;
0053: import org.eclipse.jdt.internal.compiler.ast.AbstractVariableDeclaration;
0054: import org.eclipse.jdt.internal.compiler.ast.Annotation;
0055: import org.eclipse.jdt.internal.compiler.ast.AnnotationMethodDeclaration;
0056: import org.eclipse.jdt.internal.compiler.ast.Argument;
0057: import org.eclipse.jdt.internal.compiler.ast.ArrayInitializer;
0058: import org.eclipse.jdt.internal.compiler.ast.ClassLiteralAccess;
0059: import org.eclipse.jdt.internal.compiler.ast.Clinit;
0060: import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration;
0061: import org.eclipse.jdt.internal.compiler.ast.ConditionalExpression;
0062: import org.eclipse.jdt.internal.compiler.ast.Expression;
0063: import org.eclipse.jdt.internal.compiler.ast.FalseLiteral;
0064: import org.eclipse.jdt.internal.compiler.ast.FieldDeclaration;
0065: import org.eclipse.jdt.internal.compiler.ast.Initializer;
0066: import org.eclipse.jdt.internal.compiler.ast.Javadoc;
0067: import org.eclipse.jdt.internal.compiler.ast.MagicLiteral;
0068: import org.eclipse.jdt.internal.compiler.ast.MemberValuePair;
0069: import org.eclipse.jdt.internal.compiler.ast.MethodDeclaration;
0070: import org.eclipse.jdt.internal.compiler.ast.NameReference;
0071: import org.eclipse.jdt.internal.compiler.ast.NullLiteral;
0072: import org.eclipse.jdt.internal.compiler.ast.NumberLiteral;
0073: import org.eclipse.jdt.internal.compiler.ast.SingleMemberAnnotation;
0074: import org.eclipse.jdt.internal.compiler.ast.StringLiteral;
0075: import org.eclipse.jdt.internal.compiler.ast.TrueLiteral;
0076: import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration;
0077: import org.eclipse.jdt.internal.compiler.ast.TypeParameter;
0078: import org.eclipse.jdt.internal.compiler.ast.TypeReference;
0079: import org.eclipse.jdt.internal.compiler.ast.Wildcard;
0080: import org.eclipse.jdt.internal.compiler.env.ICompilationUnit;
0081: import org.eclipse.jdt.internal.compiler.env.IGenericType;
0082: import org.eclipse.jdt.internal.compiler.impl.Constant;
0083: import org.eclipse.jdt.internal.compiler.lookup.ArrayBinding;
0084: import org.eclipse.jdt.internal.compiler.lookup.BaseTypeBinding;
0085: import org.eclipse.jdt.internal.compiler.lookup.BinaryTypeBinding;
0086: import org.eclipse.jdt.internal.compiler.lookup.BlockScope;
0087: import org.eclipse.jdt.internal.compiler.lookup.ClassScope;
0088: import org.eclipse.jdt.internal.compiler.lookup.CompilationUnitScope;
0089: import org.eclipse.jdt.internal.compiler.lookup.LocalTypeBinding;
0090: import org.eclipse.jdt.internal.compiler.lookup.MethodBinding;
0091: import org.eclipse.jdt.internal.compiler.lookup.ParameterizedTypeBinding;
0092: import org.eclipse.jdt.internal.compiler.lookup.RawTypeBinding;
0093: import org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding;
0094: import org.eclipse.jdt.internal.compiler.lookup.SourceTypeBinding;
0095: import org.eclipse.jdt.internal.compiler.lookup.TypeBinding;
0096: import org.eclipse.jdt.internal.compiler.lookup.TypeIds;
0097: import org.eclipse.jdt.internal.compiler.lookup.TypeVariableBinding;
0098: import org.eclipse.jdt.internal.compiler.lookup.WildcardBinding;
0099:
0100: import java.io.BufferedReader;
0101: import java.io.CharArrayReader;
0102: import java.io.File;
0103: import java.io.IOException;
0104: import java.lang.reflect.Array;
0105: import java.lang.reflect.Method;
0106: import java.util.ArrayList;
0107: import java.util.Arrays;
0108: import java.util.HashMap;
0109: import java.util.HashSet;
0110: import java.util.Iterator;
0111: import java.util.List;
0112: import java.util.Map;
0113: import java.util.Set;
0114: import java.util.regex.Pattern;
0115:
0116: /**
0117: * Builds a {@link com.google.gwt.core.ext.typeinfo.TypeOracle} from a set of
0118: * compilation units.
0119: * <p>
0120: * For example,
0121: *
0122: * <pre>
0123: * TypeOracleBuilder b = new TypeOracleBuilder();
0124: * b.addCompilationUnit(unit1);
0125: * b.addCompilationUnit(unit2);
0126: * b.addCompilationUnit(unit3);
0127: * b.excludePackage("example.pkg");
0128: * TypeOracle oracle = b.build(logger);
0129: * JClassType[] allTypes = oracle.getTypes();
0130: * </pre>
0131: */
0132: public class TypeOracleBuilder {
0133: private static final JClassType[] NO_JCLASSES = new JClassType[0];
0134: private static final Pattern PATTERN_WHITESPACE = Pattern
0135: .compile("\\s");
0136:
0137: public static String computeBinaryClassName(JType type) {
0138: JPrimitiveType primitiveType = type.isPrimitive();
0139: if (primitiveType != null) {
0140: return primitiveType.getJNISignature();
0141: }
0142:
0143: JArrayType arrayType = type.isArray();
0144: if (arrayType != null) {
0145: JType component = arrayType.getComponentType();
0146: if (component.isClassOrInterface() != null) {
0147: return "[L"
0148: + computeBinaryClassName(arrayType
0149: .getComponentType()) + ";";
0150: } else {
0151: return "["
0152: + computeBinaryClassName(arrayType
0153: .getComponentType());
0154: }
0155: }
0156:
0157: JParameterizedType parameterizedType = type.isParameterized();
0158: if (parameterizedType != null) {
0159: return computeBinaryClassName(parameterizedType
0160: .getBaseType());
0161: }
0162:
0163: JClassType classType = type.isClassOrInterface();
0164: assert (classType != null);
0165:
0166: JClassType enclosingType = classType.getEnclosingType();
0167: if (enclosingType != null) {
0168: return computeBinaryClassName(enclosingType) + "$"
0169: + classType.getSimpleSourceName();
0170: }
0171:
0172: return classType.getQualifiedSourceName();
0173: }
0174:
0175: static boolean parseMetaDataTags(char[] unitSource,
0176: HasMetaData hasMetaData, Javadoc javadoc) {
0177:
0178: int start = javadoc.sourceStart;
0179: int end = javadoc.sourceEnd;
0180: char[] comment = CharOperation.subarray(unitSource, start,
0181: end + 1);
0182: if (comment == null) {
0183: comment = new char[0];
0184: }
0185: BufferedReader reader = new BufferedReader(new CharArrayReader(
0186: comment));
0187: String activeTag = null;
0188: final List<String> tagValues = new ArrayList<String>();
0189: try {
0190: String line = reader.readLine();
0191: boolean firstLine = true;
0192: while (line != null) {
0193: if (firstLine) {
0194: firstLine = false;
0195: int commentStart = line.indexOf("/**");
0196: if (commentStart == -1) {
0197: // Malformed.
0198: return false;
0199: }
0200: line = line.substring(commentStart + 3);
0201: }
0202:
0203: String[] tokens = PATTERN_WHITESPACE.split(line);
0204: boolean canIgnoreStar = true;
0205: for (int i = 0; i < tokens.length; i++) {
0206: String token = tokens[i];
0207:
0208: // Check for the end.
0209: //
0210: if (token.endsWith("*/")) {
0211: token = token.substring(0, token.length() - 2);
0212: }
0213:
0214: // Check for an ignored leading star.
0215: //
0216: if (canIgnoreStar && token.startsWith("*")) {
0217: token = token.substring(1);
0218: canIgnoreStar = false;
0219: }
0220:
0221: // Decide what to do with whatever is left.
0222: //
0223: if (token.length() > 0) {
0224: canIgnoreStar = false;
0225: if (token.startsWith("@")) {
0226: // A new tag has been introduced.
0227: // Subsequent tokens will attach to it.
0228: // Make sure we finish the previously active tag before moving on.
0229: //
0230: if (activeTag != null) {
0231: finishTag(hasMetaData, activeTag,
0232: tagValues);
0233: }
0234: activeTag = token.substring(1);
0235: } else if (activeTag != null) {
0236: // Attach this item to the active tag.
0237: //
0238: tagValues.add(token);
0239: } else {
0240: // Just ignore it.
0241: //
0242: }
0243: }
0244: }
0245:
0246: line = reader.readLine();
0247: }
0248: } catch (IOException e) {
0249: return false;
0250: }
0251:
0252: // To catch the last batch of values, if any.
0253: //
0254: finishTag(hasMetaData, activeTag, tagValues);
0255: return true;
0256: }
0257:
0258: private static void finishTag(HasMetaData hasMetaData,
0259: String tagName, List<String> tagValues) {
0260: // Add the values even if the list is empty, because the presence of the
0261: // tag itself might be important.
0262: //
0263: String[] values = tagValues.toArray(Empty.STRINGS);
0264: hasMetaData.addMetaData(tagName, values);
0265: tagValues.clear();
0266: }
0267:
0268: /**
0269: * Returns the value associated with a JDT constant.
0270: */
0271: private static Object getConstantValue(Constant constant) {
0272: switch (constant.typeID()) {
0273: case TypeIds.T_char:
0274: return constant.charValue();
0275: case TypeIds.T_byte:
0276: return constant.byteValue();
0277: case TypeIds.T_short:
0278: return constant.shortValue();
0279: case TypeIds.T_boolean:
0280: return constant.booleanValue();
0281: case TypeIds.T_long:
0282: return constant.longValue();
0283: case TypeIds.T_double:
0284: return constant.doubleValue();
0285: case TypeIds.T_float:
0286: return constant.floatValue();
0287: case TypeIds.T_int:
0288: return constant.intValue();
0289: case TypeIds.T_JavaLangString:
0290: return constant.stringValue();
0291: case TypeIds.T_null:
0292: return null;
0293: default:
0294: break;
0295: }
0296:
0297: assert false : "Unknown constant type";
0298: return null;
0299: }
0300:
0301: private static String getMethodName(JClassType enclosingType,
0302: AbstractMethodDeclaration jmethod) {
0303: if (jmethod.isConstructor()) {
0304: return String.valueOf(enclosingType.getSimpleSourceName());
0305: } else {
0306: return String.valueOf(jmethod.binding.selector);
0307: }
0308: }
0309:
0310: /**
0311: * Returns the number associated with a JDT numeric literal.
0312: */
0313: private static Object getNumericLiteralValue(
0314: NumberLiteral expression) {
0315: return getConstantValue(expression.constant);
0316: }
0317:
0318: private static boolean isAnnotation(TypeDeclaration typeDecl) {
0319: if (typeDecl.kind() == IGenericType.ANNOTATION_TYPE_DECL) {
0320: return true;
0321: } else {
0322: return false;
0323: }
0324: }
0325:
0326: /**
0327: * Returns <code>true</code> if this name is the special package-info type
0328: * name.
0329: *
0330: * @return <code>true</code> if this name is the special package-info type
0331: * name
0332: */
0333: private static boolean isPackageInfoTypeName(String qname) {
0334: return "package-info".equals(qname);
0335: }
0336:
0337: private static boolean maybeGeneric(TypeDeclaration typeDecl,
0338: JClassType enclosingType) {
0339:
0340: if (enclosingType != null
0341: && enclosingType.isGenericType() != null) {
0342: if (!typeDecl.binding.isStatic()) {
0343: /*
0344: * This non-static, inner class can reference the type parameters of its
0345: * enclosing generic type, so we will treat it as a generic type.
0346: */
0347: return true;
0348: }
0349: }
0350:
0351: if (typeDecl.binding.isLocalType()) {
0352: LocalTypeBinding localTypeBinding = (LocalTypeBinding) typeDecl.binding;
0353: MethodBinding enclosingMethod = localTypeBinding.enclosingMethod;
0354: if (enclosingMethod != null) {
0355: if (enclosingMethod.typeVariables != null
0356: && enclosingMethod.typeVariables.length != 0) {
0357: /*
0358: * This local type can reference the type parameters of its enclosing
0359: * method, so we will treat is as a generic type.
0360: */
0361: return true;
0362: }
0363: }
0364: }
0365:
0366: if (typeDecl.typeParameters != null) {
0367: // Definitely generic since it has type parameters.
0368: return true;
0369: }
0370:
0371: return false;
0372: }
0373:
0374: // TODO: move into the Annotations class or create an AnnotationsUtil class?
0375: private static HashMap<Class<? extends java.lang.annotation.Annotation>, java.lang.annotation.Annotation> newAnnotationMap() {
0376: return new HashMap<Class<? extends java.lang.annotation.Annotation>, java.lang.annotation.Annotation>();
0377: }
0378:
0379: private static void removeInfectedUnits(final TreeLogger logger,
0380: final Map<String, CompilationUnitDeclaration> cudsByFileName) {
0381:
0382: final Set<String> pendingRemovals = new HashSet<String>();
0383: TypeRefVisitor trv = new TypeRefVisitor() {
0384: @Override
0385: protected void onTypeRef(SourceTypeBinding referencedType,
0386: CompilationUnitDeclaration unitOfReferrer) {
0387: // If the referenced type belongs to a compilation unit that is
0388: // not in the list of valid units, then the unit in which it
0389: // is referenced must also be removed.
0390: //
0391: String referencedFn = String.valueOf(referencedType
0392: .getFileName());
0393: CompilationUnitDeclaration referencedCud = cudsByFileName
0394: .get(referencedFn);
0395: if (referencedCud == null) {
0396: // This is a referenced to a bad or non-existent unit.
0397: // So, remove the referrer's unit if it hasn't been already.
0398: //
0399: String referrerFn = String.valueOf(unitOfReferrer
0400: .getFileName());
0401: if (cudsByFileName.containsKey(referrerFn)
0402: && !pendingRemovals.contains(referrerFn)) {
0403: TreeLogger branch = logger.branch(
0404: TreeLogger.TRACE,
0405: "Cascaded removal of compilation unit '"
0406: + referrerFn + "'", null);
0407: final String badTypeName = CharOperation
0408: .toString(referencedType.compoundName);
0409: branch.branch(TreeLogger.TRACE,
0410: "Due to reference to unavailable type: "
0411: + badTypeName, null);
0412: pendingRemovals.add(referrerFn);
0413: }
0414: }
0415: }
0416: };
0417:
0418: do {
0419: // Perform any pending removals.
0420: //
0421: for (Iterator<String> iter = pendingRemovals.iterator(); iter
0422: .hasNext();) {
0423: String fnToRemove = iter.next();
0424: Object removed = cudsByFileName.remove(fnToRemove);
0425: assert (removed != null);
0426: }
0427:
0428: // Start fresh for this iteration.
0429: //
0430: pendingRemovals.clear();
0431:
0432: // Find references to type in units that aren't valid.
0433: //
0434: for (Iterator<CompilationUnitDeclaration> iter = cudsByFileName
0435: .values().iterator(); iter.hasNext();) {
0436: CompilationUnitDeclaration cud = iter.next();
0437: cud.traverse(trv, cud.scope);
0438: }
0439: } while (!pendingRemovals.isEmpty());
0440: }
0441:
0442: private static void removeUnitsWithErrors(TreeLogger logger,
0443: Map<String, CompilationUnitDeclaration> cudsByFileName) {
0444: // Start by removing units with a known problem.
0445: //
0446: boolean anyRemoved = false;
0447: for (Iterator<CompilationUnitDeclaration> iter = cudsByFileName
0448: .values().iterator(); iter.hasNext();) {
0449: CompilationUnitDeclaration cud = iter.next();
0450: CompilationResult result = cud.compilationResult;
0451: IProblem[] errors = result.getErrors();
0452: if (errors != null && errors.length > 0) {
0453: anyRemoved = true;
0454: iter.remove();
0455:
0456: String fileName = CharOperation.charToString(cud
0457: .getFileName());
0458: char[] source = cud.compilationResult.compilationUnit
0459: .getContents();
0460: Util.maybeDumpSource(logger, fileName, source, null);
0461: logger.log(TreeLogger.TRACE,
0462: "Removing problematic compilation unit '"
0463: + fileName + "'", null);
0464: }
0465: }
0466:
0467: if (anyRemoved) {
0468: // Then removing anything else that won't compile as a result.
0469: //
0470: removeInfectedUnits(logger, cudsByFileName);
0471: }
0472: }
0473:
0474: private final CacheManager cacheManager;
0475:
0476: /**
0477: * Constructs a default instance, with a default cacheManager. This is not to
0478: * be used in Hosted Mode, as caching will then not work.
0479: */
0480: public TypeOracleBuilder() {
0481: cacheManager = new CacheManager();
0482: }
0483:
0484: /**
0485: * Constructs an instance from the supplied cacheManager, using the
0486: * <code>TypeOracle</code> contained therein. This is to be used in Hosted
0487: * Mode, so that caching will work, assuming the cacheManager has a cache
0488: * directory.
0489: */
0490: public TypeOracleBuilder(CacheManager cacheManager) {
0491: this .cacheManager = cacheManager;
0492: }
0493:
0494: /**
0495: * Constructs an instance from the supplied typeOracle, with a cacheManager
0496: * using the same typeOracle. This is not to be used in Hosted Mode, as
0497: * caching will then not work.
0498: */
0499: public TypeOracleBuilder(TypeOracle typeOracle) {
0500: cacheManager = new CacheManager(typeOracle);
0501: }
0502:
0503: /**
0504: * Includes the specified logical compilation unit into the set of units this
0505: * builder will parse and analyze. If a previous compilation unit was
0506: * specified in the same location, it will be replaced if it is older.
0507: */
0508: public void addCompilationUnit(CompilationUnitProvider cup)
0509: throws UnableToCompleteException {
0510: cacheManager.addCompilationUnit(cup);
0511: }
0512:
0513: public TypeOracle build(final TreeLogger logger)
0514: throws UnableToCompleteException {
0515: Set<CompilationUnitProvider> addedCups = cacheManager
0516: .getAddedCups();
0517: TypeOracle oracle = cacheManager.getTypeOracle();
0518: // Make a copy that we can sort.
0519: //
0520: for (Iterator<CompilationUnitProvider> iter = addedCups
0521: .iterator(); iter.hasNext();) {
0522: CompilationUnitProvider cup = iter.next();
0523: String location = cup.getLocation();
0524: if (!((location.indexOf("http://") != -1) || (location
0525: .indexOf("ftp://") != -1))) {
0526: location = Util.findFileName(location);
0527: if (!(new File(location).exists() || cup.isTransient())) {
0528: iter.remove();
0529: logger
0530: .log(
0531: TreeLogger.TRACE,
0532: "The file "
0533: + location
0534: + " was removed by the user. All types therein are now unavailable.",
0535: null);
0536: }
0537: }
0538: }
0539: CompilationUnitProvider[] cups = Util.toArray(
0540: CompilationUnitProvider.class, addedCups);
0541: Arrays.sort(cups, CompilationUnitProvider.LOCATION_COMPARATOR);
0542:
0543: // Make sure we can find the java.lang.Object compilation unit.
0544: //
0545: boolean foundJavaLangPackage = oracle.findPackage("java.lang") != null;
0546:
0547: // Adapt to JDT idioms.
0548: //
0549: ICompilationUnit[] units = new ICompilationUnit[cups.length];
0550: for (int i = 0; i < cups.length; i++) {
0551: if (!foundJavaLangPackage
0552: && cups[i].getPackageName().equals("java.lang")) {
0553: foundJavaLangPackage = true;
0554: }
0555: units[i] = cacheManager.findUnitForCup(cups[i]);
0556: }
0557:
0558: // Error if no java.lang.
0559: if (!foundJavaLangPackage) {
0560: Util.logMissingTypeErrorWithHints(logger,
0561: "java.lang.Object");
0562: throw new UnableToCompleteException();
0563: }
0564: cacheManager.invalidateOnRefresh(oracle);
0565: CompilationUnitDeclaration[] cuds = cacheManager
0566: .getAstCompiler().getCompilationUnitDeclarations(
0567: logger, units);
0568:
0569: // Build a list that makes it easy to remove problems.
0570: //
0571: final Map<String, CompilationUnitDeclaration> cudsByFileName = new HashMap<String, CompilationUnitDeclaration>();
0572: for (int i = 0; i < cuds.length; i++) {
0573: CompilationUnitDeclaration cud = cuds[i];
0574: char[] location = cud.getFileName();
0575: cudsByFileName.put(String.valueOf(location), cud);
0576: }
0577: cacheManager.getCudsByFileName().putAll(cudsByFileName);
0578:
0579: // Remove bad cuds and all the other cuds that are affected.
0580: //
0581: removeUnitsWithErrors(logger, cudsByFileName);
0582:
0583: // Also remove any compilation units that we've seen before.
0584: //
0585: for (Iterator<CompilationUnitDeclaration> iter = cudsByFileName
0586: .values().iterator(); iter.hasNext();) {
0587: CompilationUnitDeclaration cud = iter.next();
0588: // If we've seen this compilation unit before, the type oracle will
0589: // tell us about it and so we don't assimilate it again.
0590: //
0591: ICompilationUnit unit = cud.compilationResult.compilationUnit;
0592: ICompilationUnitAdapter adapter = ((ICompilationUnitAdapter) unit);
0593: CompilationUnitProvider cup = adapter
0594: .getCompilationUnitProvider();
0595: JClassType[] seen = oracle.getTypesInCompilationUnit(cup);
0596: if (seen.length > 0) {
0597: // This compilation unit has already been assimilated.
0598: //
0599: iter.remove();
0600: }
0601: }
0602:
0603: // Perform a shallow pass to establish identity for new types.
0604: //
0605: final CacheManager.Mapper identityMapper = cacheManager
0606: .getIdentityMapper();
0607: for (Iterator<CompilationUnitDeclaration> iter = cudsByFileName
0608: .values().iterator(); iter.hasNext();) {
0609: CompilationUnitDeclaration cud = iter.next();
0610:
0611: cud.traverse(new ASTVisitor() {
0612: @Override
0613: public boolean visit(TypeDeclaration typeDecl,
0614: BlockScope scope) {
0615: JClassType enclosingType = identityMapper
0616: .get(typeDecl.binding.enclosingType());
0617: processType(typeDecl, enclosingType, true);
0618: return true;
0619: }
0620:
0621: @Override
0622: public boolean visit(TypeDeclaration typeDecl,
0623: ClassScope scope) {
0624: JClassType enclosingType = identityMapper
0625: .get(typeDecl.binding.enclosingType());
0626: processType(typeDecl, enclosingType, false);
0627: return true;
0628: }
0629:
0630: @Override
0631: public boolean visit(TypeDeclaration typeDecl,
0632: CompilationUnitScope scope) {
0633: processType(typeDecl, null, false);
0634: return true;
0635: }
0636:
0637: }, cud.scope);
0638: }
0639:
0640: // Perform a deep pass to resolve all types in terms of our types.
0641: //
0642: for (Iterator<CompilationUnitDeclaration> iter = cudsByFileName
0643: .values().iterator(); iter.hasNext();) {
0644: CompilationUnitDeclaration cud = iter.next();
0645: String loc = String.valueOf(cud.getFileName());
0646: String processing = "Processing types in compilation unit: "
0647: + loc;
0648: final TreeLogger cudLogger = logger.branch(TreeLogger.SPAM,
0649: processing, null);
0650: final char[] source = cud.compilationResult.compilationUnit
0651: .getContents();
0652:
0653: cud.traverse(new ASTVisitor() {
0654: @Override
0655: public boolean visit(TypeDeclaration typeDecl,
0656: BlockScope scope) {
0657: if (!resolveTypeDeclaration(cudLogger, source,
0658: typeDecl)) {
0659: String name = String.valueOf(typeDecl.binding
0660: .readableName());
0661: String msg = "Unexpectedly unable to fully resolve type "
0662: + name;
0663: logger.log(TreeLogger.WARN, msg, null);
0664: }
0665: return true;
0666: }
0667:
0668: @Override
0669: public boolean visit(TypeDeclaration typeDecl,
0670: ClassScope scope) {
0671: if (!resolveTypeDeclaration(cudLogger, source,
0672: typeDecl)) {
0673: String name = String.valueOf(typeDecl.binding
0674: .readableName());
0675: String msg = "Unexpectedly unable to fully resolve type "
0676: + name;
0677: logger.log(TreeLogger.WARN, msg, null);
0678: }
0679: return true;
0680: }
0681:
0682: @Override
0683: public boolean visit(TypeDeclaration typeDecl,
0684: CompilationUnitScope scope) {
0685: if (!resolveTypeDeclaration(cudLogger, source,
0686: typeDecl)) {
0687: String name = String.valueOf(typeDecl.binding
0688: .readableName());
0689: String msg = "Unexpectedly unable to fully resolve type "
0690: + name;
0691: logger.log(TreeLogger.WARN, msg, null);
0692: }
0693: return true;
0694: }
0695: }, cud.scope);
0696: }
0697: Util.invokeInaccessableMethod(TypeOracle.class, "refresh",
0698: new Class[] { TreeLogger.class }, oracle,
0699: new Object[] { logger });
0700: return oracle;
0701: }
0702:
0703: private Object createAnnotationInstance(TreeLogger logger,
0704: Expression expression) {
0705: Annotation annotation = (Annotation) expression;
0706:
0707: // Determine the annotation class
0708: TypeBinding resolvedType = annotation.resolvedType;
0709: Class<?> classLiteral = getClassLiteral(logger, resolvedType);
0710: if (classLiteral == null) {
0711: return null;
0712: }
0713:
0714: Class<? extends java.lang.annotation.Annotation> clazz = classLiteral
0715: .asSubclass(java.lang.annotation.Annotation.class);
0716:
0717: // Build the map of identifiers to values.
0718: Map<String, Object> identifierToValue = new HashMap<String, Object>();
0719: for (MemberValuePair mvp : annotation.memberValuePairs()) {
0720: // Method name
0721: String identifier = String.valueOf(mvp.name);
0722:
0723: // Value
0724: Expression expressionValue = mvp.value;
0725: Object value = evaluateConstantExpression(logger,
0726: expressionValue);
0727: if (value == null) {
0728: return null;
0729: }
0730:
0731: try {
0732: Method method = clazz.getMethod(identifier,
0733: new Class[0]);
0734: Class<?> expectedClass = method.getReturnType();
0735: Class<? extends Object> actualClass = value.getClass();
0736: if (expectedClass.isArray() && !actualClass.isArray()) {
0737: /*
0738: * JSL3 Section 9.7 single element annotations can skip the curly
0739: * braces; means we do not get an array
0740: */
0741: assert (expression instanceof SingleMemberAnnotation);
0742: Object array = Array.newInstance(expectedClass
0743: .getComponentType(), 1);
0744: Array.set(array, 0, value);
0745: value = array;
0746: }
0747: } catch (SecurityException e) {
0748: return null;
0749: } catch (NoSuchMethodException e) {
0750: return null;
0751: }
0752:
0753: identifierToValue.put(identifier, value);
0754: }
0755:
0756: // Create the Annotation proxy
0757: JClassType annotationType = (JClassType) resolveType(logger,
0758: resolvedType);
0759: if (annotationType == null) {
0760: return null;
0761: }
0762:
0763: return AnnotationProxyFactory.create(clazz, annotationType,
0764: identifierToValue);
0765: }
0766:
0767: private Object createArrayConstant(TreeLogger logger,
0768: ArrayInitializer arrayInitializer) {
0769: Class<?> leafComponentClass = getClassLiteral(logger,
0770: arrayInitializer.binding.leafComponentType);
0771: if (leafComponentClass == null) {
0772: return null;
0773: }
0774:
0775: int[] dimensions = new int[arrayInitializer.binding.dimensions];
0776:
0777: Expression[] initExpressions = arrayInitializer.expressions;
0778: if (initExpressions != null) {
0779: dimensions[0] = initExpressions.length;
0780: }
0781:
0782: Object array = Array
0783: .newInstance(leafComponentClass, dimensions);
0784: boolean failed = false;
0785: if (initExpressions != null) {
0786: for (int i = 0; i < initExpressions.length; ++i) {
0787: Expression arrayInitExp = initExpressions[i];
0788: Object value = evaluateConstantExpression(logger,
0789: arrayInitExp);
0790: if (value != null) {
0791: Array.set(array, i, value);
0792: } else {
0793: failed = true;
0794: break;
0795: }
0796: }
0797: }
0798:
0799: if (!failed) {
0800: return array;
0801: }
0802:
0803: return null;
0804: }
0805:
0806: private JUpperBound createTypeVariableBounds(TreeLogger logger,
0807: TypeVariableBinding tvBinding) {
0808: TypeBinding jfirstBound = tvBinding.firstBound;
0809: if (jfirstBound == null) {
0810: // No bounds were specified, so we default to Object
0811: JClassType upperBound = (JClassType) resolveType(logger,
0812: tvBinding.super class);
0813: /*
0814: * Can't test for equality with TypeOracle.getJavaLangObject() since it
0815: * may not be initialized at this point
0816: */
0817: assert (Object.class.getName().equals(upperBound
0818: .getQualifiedSourceName()));
0819: return new JUpperBound(upperBound);
0820: }
0821:
0822: List<JClassType> bounds = new ArrayList<JClassType>();
0823: JClassType firstBound = (JClassType) resolveType(logger,
0824: jfirstBound);
0825: if (firstBound.isClass() != null) {
0826: bounds.add(firstBound);
0827: }
0828:
0829: ReferenceBinding[] jsuper Interfaces = tvBinding
0830: .super Interfaces();
0831: for (ReferenceBinding jsuper Interface : jsuper Interfaces) {
0832: JClassType super Interface = (JClassType) resolveType(
0833: logger, jsuper Interface);
0834: assert (super Interface != null);
0835: assert (super Interface.isInterface() != null);
0836: bounds.add(super Interface);
0837: }
0838:
0839: return new JUpperBound(bounds.toArray(NO_JCLASSES));
0840: }
0841:
0842: private Object evaluateConstantExpression(TreeLogger logger,
0843: Expression expression) {
0844: if (expression instanceof MagicLiteral) {
0845: if (expression instanceof FalseLiteral) {
0846: return Boolean.FALSE;
0847: } else if (expression instanceof NullLiteral) {
0848: // null is not a valid annotation value; JLS Third Ed. Section 9.7
0849: } else if (expression instanceof TrueLiteral) {
0850: return Boolean.TRUE;
0851: }
0852: } else if (expression instanceof NumberLiteral) {
0853: Object value = getNumericLiteralValue((NumberLiteral) expression);
0854: if (value != null) {
0855: return value;
0856: }
0857: } else if (expression instanceof StringLiteral) {
0858: StringLiteral stringLiteral = (StringLiteral) expression;
0859: return stringLiteral.constant.stringValue();
0860: } else if (expression instanceof ClassLiteralAccess) {
0861: ClassLiteralAccess classLiteral = (ClassLiteralAccess) expression;
0862: Class<?> clazz = getClassLiteral(logger,
0863: classLiteral.targetType);
0864: if (clazz != null) {
0865: return clazz;
0866: }
0867: } else if (expression instanceof ArrayInitializer) {
0868: Object value = createArrayConstant(logger,
0869: (ArrayInitializer) expression);
0870: if (value != null) {
0871: return value;
0872: }
0873: } else if (expression instanceof NameReference) {
0874: /*
0875: * This name reference can only be to something that is a constant value,
0876: * or an enumerated type since annotation values must be constants.
0877: */
0878: NameReference nameRef = (NameReference) expression;
0879:
0880: if (nameRef.constant != Constant.NotAConstant) {
0881: return getConstantValue(nameRef.constant);
0882: } else {
0883: Class clazz = getClassLiteral(logger,
0884: nameRef.actualReceiverType);
0885: if (clazz.isEnum()) {
0886: String enumName = String.valueOf(nameRef
0887: .fieldBinding().name);
0888: return Enum.valueOf(clazz, enumName);
0889: } else {
0890: /*
0891: * If this is not an enumeration, then fall through to failure case
0892: * below.
0893: */
0894: }
0895: }
0896: } else if (expression instanceof Annotation) {
0897: Object annotationInstance = createAnnotationInstance(
0898: logger, expression);
0899: if (annotationInstance != null) {
0900: return annotationInstance;
0901: }
0902: } else if (expression instanceof ConditionalExpression) {
0903: ConditionalExpression condExpression = (ConditionalExpression) expression;
0904: assert (condExpression.constant != Constant.NotAConstant);
0905:
0906: return getConstantValue(condExpression.constant);
0907: }
0908:
0909: assert (false);
0910: return null;
0911: }
0912:
0913: private Class<?> getClassLiteral(TreeLogger logger,
0914: TypeBinding resolvedType) {
0915: JType type = resolveType(logger, resolvedType);
0916: if (type == null) {
0917: return null;
0918: }
0919:
0920: if (type.isPrimitive() != null) {
0921: return getClassLiteralForPrimitive(type.isPrimitive());
0922: } else {
0923: try {
0924: String className = computeBinaryClassName(type);
0925: Class<?> clazz = Class.forName(className);
0926: return clazz;
0927: } catch (ClassNotFoundException e) {
0928: logger.log(TreeLogger.ERROR, "", e);
0929: return null;
0930: }
0931: }
0932: }
0933:
0934: private Class<?> getClassLiteralForPrimitive(JPrimitiveType type) {
0935: if (type == JPrimitiveType.BOOLEAN) {
0936: return boolean.class;
0937: } else if (type == JPrimitiveType.BYTE) {
0938: return byte.class;
0939: } else if (type == JPrimitiveType.CHAR) {
0940: return char.class;
0941: } else if (type == JPrimitiveType.DOUBLE) {
0942: return double.class;
0943: } else if (type == JPrimitiveType.FLOAT) {
0944: return float.class;
0945: } else if (type == JPrimitiveType.INT) {
0946: return int.class;
0947: } else if (type == JPrimitiveType.LONG) {
0948: return long.class;
0949: } else if (type == JPrimitiveType.SHORT) {
0950: return short.class;
0951: }
0952:
0953: assert (false);
0954: return null;
0955: }
0956:
0957: private CompilationUnitProvider getCup(TypeDeclaration typeDecl) {
0958: ICompilationUnit icu = typeDecl.compilationResult.compilationUnit;
0959: ICompilationUnitAdapter icua = (ICompilationUnitAdapter) icu;
0960: return icua.getCompilationUnitProvider();
0961: }
0962:
0963: private String getPackage(TypeDeclaration typeDecl) {
0964: final char[][] pkgParts = typeDecl.compilationResult.compilationUnit
0965: .getPackageName();
0966: return String.valueOf(CharOperation.concatWith(pkgParts, '.'));
0967: }
0968:
0969: /**
0970: * Returns the qualified name of the binding, excluding any type parameter
0971: * information.
0972: */
0973: private String getQualifiedName(ReferenceBinding binding) {
0974: String qualifiedName = CharOperation
0975: .toString(binding.compoundName);
0976: if (binding instanceof LocalTypeBinding) {
0977: // The real name of a local type is its constant pool name.
0978: qualifiedName = CharOperation.charToString(binding
0979: .constantPoolName());
0980: qualifiedName = qualifiedName.replace('/', '.');
0981: } else {
0982: /*
0983: * All other types have their fully qualified name as part of its compound
0984: * name.
0985: */
0986: qualifiedName = CharOperation
0987: .toString(binding.compoundName);
0988: }
0989:
0990: qualifiedName = qualifiedName.replace('$', '.');
0991:
0992: return qualifiedName;
0993: }
0994:
0995: private String getSimpleName(TypeDeclaration typeDecl) {
0996: return String.valueOf(typeDecl.name);
0997: }
0998:
0999: private boolean isInterface(TypeDeclaration typeDecl) {
1000: if (typeDecl.kind() == IGenericType.INTERFACE_DECL) {
1001: return true;
1002: } else {
1003: return false;
1004: }
1005: }
1006:
1007: /**
1008: * Maps a TypeDeclaration into a JClassType.
1009: */
1010: private void processType(TypeDeclaration typeDecl,
1011: JClassType enclosingType, boolean isLocalType) {
1012: TypeOracle oracle = cacheManager.getTypeOracle();
1013:
1014: // Create our version of the type structure unless it already exists in the
1015: // type oracle.
1016: //
1017: SourceTypeBinding binding = typeDecl.binding;
1018: if (binding.constantPoolName() == null) {
1019: /*
1020: * Weird case: if JDT determines that this local class is totally
1021: * uninstantiable, it won't bother allocating a local name.
1022: */
1023: return;
1024: }
1025:
1026: String qname = getQualifiedName(binding);
1027: String jclassName;
1028: if (binding instanceof LocalTypeBinding) {
1029: jclassName = qname.substring(qname.lastIndexOf('.') + 1);
1030: } else {
1031: jclassName = getSimpleName(typeDecl);
1032: }
1033:
1034: if (oracle.findType(qname) != null) {
1035: return;
1036: }
1037:
1038: String jpkgName = getPackage(typeDecl);
1039: JPackage pkg = oracle.getOrCreatePackage(jpkgName);
1040: final boolean jclassIsIntf = isInterface(typeDecl);
1041: boolean jclassIsAnnonation = isAnnotation(typeDecl);
1042: CompilationUnitProvider cup = getCup(typeDecl);
1043:
1044: int declStart = typeDecl.declarationSourceStart;
1045: int declEnd = typeDecl.declarationSourceEnd;
1046: int bodyStart = typeDecl.bodyStart;
1047: int bodyEnd = typeDecl.bodyEnd;
1048:
1049: JRealClassType type;
1050: if (jclassIsAnnonation) {
1051: type = new JAnnotationType(oracle, cup, pkg, enclosingType,
1052: isLocalType, jclassName, declStart, declEnd,
1053: bodyStart, bodyEnd, jclassIsIntf);
1054: } else if (maybeGeneric(typeDecl, enclosingType)) {
1055: type = new JGenericType(oracle, cup, pkg, enclosingType,
1056: isLocalType, jclassName, declStart, declEnd,
1057: bodyStart, bodyEnd, jclassIsIntf);
1058: } else if (binding.isEnum()) {
1059: type = new JEnumType(oracle, cup, pkg, enclosingType,
1060: isLocalType, jclassName, declStart, declEnd,
1061: bodyStart, bodyEnd, jclassIsIntf);
1062: } else {
1063: type = new JRealClassType(oracle, cup, pkg, enclosingType,
1064: isLocalType, jclassName, declStart, declEnd,
1065: bodyStart, bodyEnd, jclassIsIntf);
1066: }
1067:
1068: cacheManager.setTypeForBinding(binding, type);
1069: }
1070:
1071: private boolean resolveAnnotation(
1072: TreeLogger logger,
1073: Annotation jannotation,
1074: Map<Class<? extends java.lang.annotation.Annotation>, java.lang.annotation.Annotation> declaredAnnotations) {
1075: // Determine the annotation class
1076: TypeBinding resolvedType = jannotation.resolvedType;
1077: Class<? extends java.lang.annotation.Annotation> clazz = (Class<? extends java.lang.annotation.Annotation>) getClassLiteral(
1078: logger, resolvedType);
1079: if (clazz == null) {
1080: return false;
1081: }
1082:
1083: java.lang.annotation.Annotation annotation = (java.lang.annotation.Annotation) createAnnotationInstance(
1084: logger, jannotation);
1085: if (annotation == null) {
1086: return false;
1087: }
1088:
1089: declaredAnnotations.put(clazz, annotation);
1090: return (annotation != null) ? true : false;
1091: }
1092:
1093: private boolean resolveAnnotations(
1094: TreeLogger logger,
1095: Annotation[] annotations,
1096: Map<Class<? extends java.lang.annotation.Annotation>, java.lang.annotation.Annotation> declaredAnnotations) {
1097: if (annotations != null) {
1098: for (Annotation annotation : annotations) {
1099: if (!resolveAnnotation(logger, annotation,
1100: declaredAnnotations)) {
1101: return false;
1102: }
1103: }
1104: }
1105: return true;
1106: }
1107:
1108: private boolean resolveField(TreeLogger logger, char[] unitSource,
1109: JClassType enclosingType, FieldDeclaration jfield) {
1110:
1111: if (jfield instanceof Initializer) {
1112: // Pretend we didn't see this.
1113: //
1114: return true;
1115: }
1116:
1117: // Resolve annotations
1118: Map<Class<? extends java.lang.annotation.Annotation>, java.lang.annotation.Annotation> declaredAnnotations = newAnnotationMap();
1119: if (!resolveAnnotations(logger, jfield.annotations,
1120: declaredAnnotations)) {
1121: // Failed to resolve.
1122: //
1123: return false;
1124: }
1125:
1126: String name = String.valueOf(jfield.name);
1127: JField field;
1128: if (jfield.getKind() == AbstractVariableDeclaration.ENUM_CONSTANT) {
1129: assert (enclosingType.isEnum() != null);
1130: field = new JEnumConstant(enclosingType, name,
1131: declaredAnnotations, jfield.binding.original().id);
1132: } else {
1133: field = new JField(enclosingType, name, declaredAnnotations);
1134: }
1135:
1136: // Get modifiers.
1137: //
1138: field.addModifierBits(Shared
1139: .bindingToModifierBits(jfield.binding));
1140:
1141: // Set the field type.
1142: //
1143: TypeBinding jfieldType = jfield.binding.type;
1144:
1145: JType fieldType = resolveType(logger, jfieldType);
1146: if (fieldType == null) {
1147: // Unresolved type.
1148: //
1149: return false;
1150: }
1151: field.setType(fieldType);
1152:
1153: // Get tags.
1154: //
1155: if (jfield.javadoc != null) {
1156: if (!parseMetaDataTags(unitSource, field, jfield.javadoc)) {
1157: return false;
1158: }
1159: }
1160:
1161: return true;
1162: }
1163:
1164: private boolean resolveFields(TreeLogger logger, char[] unitSource,
1165: JClassType type, FieldDeclaration[] jfields) {
1166: if (jfields != null) {
1167: for (int i = 0; i < jfields.length; i++) {
1168: FieldDeclaration jfield = jfields[i];
1169: if (!resolveField(logger, unitSource, type, jfield)) {
1170: return false;
1171: }
1172: }
1173: }
1174: return true;
1175: }
1176:
1177: private boolean resolveMethod(TreeLogger logger, char[] unitSource,
1178: JClassType enclosingType, AbstractMethodDeclaration jmethod) {
1179:
1180: if (jmethod instanceof Clinit) {
1181: // Pretend we didn't see this.
1182: //
1183: return true;
1184: }
1185:
1186: int declStart = jmethod.declarationSourceStart;
1187: int declEnd = jmethod.declarationSourceEnd;
1188: int bodyStart = jmethod.bodyStart;
1189: int bodyEnd = jmethod.bodyEnd;
1190: String name = getMethodName(enclosingType, jmethod);
1191:
1192: // Resolve annotations
1193: Map<Class<? extends java.lang.annotation.Annotation>, java.lang.annotation.Annotation> declaredAnnotations = newAnnotationMap();
1194: if (!resolveAnnotations(logger, jmethod.annotations,
1195: declaredAnnotations)) {
1196: // Failed to resolve.
1197: //
1198: return false;
1199: }
1200:
1201: JAbstractMethod method;
1202: if (jmethod.isConstructor()) {
1203: method = new JConstructor(enclosingType, name, declStart,
1204: declEnd, bodyStart, bodyEnd, declaredAnnotations);
1205:
1206: // Resolve the type parameters, since they may be used as the return type,
1207: // etc.
1208: if (!resolveTypeParameters(logger, method, jmethod
1209: .typeParameters())) {
1210: return false;
1211: }
1212: } else {
1213: if (jmethod.isAnnotationMethod()) {
1214: AnnotationMethodDeclaration annotationMethod = (AnnotationMethodDeclaration) jmethod;
1215: Object defaultValue = null;
1216: if (annotationMethod.defaultValue != null) {
1217: defaultValue = evaluateConstantExpression(logger,
1218: annotationMethod.defaultValue);
1219: }
1220: method = new JAnnotationMethod(enclosingType, name,
1221: declStart, declEnd, bodyStart, bodyEnd,
1222: defaultValue, declaredAnnotations);
1223: } else {
1224: method = new JMethod(enclosingType, name, declStart,
1225: declEnd, bodyStart, bodyEnd,
1226: declaredAnnotations);
1227: }
1228:
1229: // Resolve the type parameters, since they may be used as the return type,
1230: // etc.
1231: if (!resolveTypeParameters(logger, method, jmethod
1232: .typeParameters())) {
1233: return false;
1234: }
1235:
1236: TypeBinding jreturnType = ((MethodDeclaration) jmethod).returnType.resolvedType;
1237: JType returnType = resolveType(logger, jreturnType);
1238: if (returnType == null) {
1239: // Unresolved type.
1240: //
1241: return false;
1242: }
1243: ((JMethod) method).setReturnType(returnType);
1244: }
1245:
1246: // Parse modifiers.
1247: //
1248: method.addModifierBits(Shared
1249: .bindingToModifierBits(jmethod.binding));
1250: if (enclosingType.isInterface() != null) {
1251: // Always add implicit modifiers on interface methods.
1252: //
1253: method.addModifierBits(Shared.MOD_PUBLIC
1254: | Shared.MOD_ABSTRACT);
1255: }
1256:
1257: // Add the parameters.
1258: //
1259: Argument[] jparams = jmethod.arguments;
1260: if (!resolveParameters(logger, method, jparams)) {
1261: return false;
1262: }
1263:
1264: // Add throws.
1265: //
1266: TypeReference[] jthrows = jmethod.thrownExceptions;
1267: if (!resolveThrownTypes(logger, method, jthrows)) {
1268: return false;
1269: }
1270:
1271: // Get tags.
1272: //
1273: if (jmethod.javadoc != null) {
1274: if (!parseMetaDataTags(unitSource, method, jmethod.javadoc)) {
1275: return false;
1276: }
1277: }
1278:
1279: return true;
1280: }
1281:
1282: private boolean resolveMethods(TreeLogger logger,
1283: char[] unitSource, JClassType type,
1284: AbstractMethodDeclaration[] jmethods) {
1285: if (jmethods != null) {
1286: for (int i = 0; i < jmethods.length; i++) {
1287: AbstractMethodDeclaration jmethod = jmethods[i];
1288: if (!resolveMethod(logger, unitSource, type, jmethod)) {
1289: return false;
1290: }
1291: }
1292: }
1293: return true;
1294: }
1295:
1296: private boolean resolvePackage(TreeLogger logger,
1297: TypeDeclaration jclass) {
1298: SourceTypeBinding binding = jclass.binding;
1299:
1300: TypeOracle oracle = cacheManager.getTypeOracle();
1301: String packageName = String.valueOf(binding.fPackage
1302: .readableName());
1303: JPackage pkg = oracle.getOrCreatePackage(packageName);
1304: assert (pkg != null);
1305:
1306: CompilationUnitScope cus = (CompilationUnitScope) jclass.scope.parent;
1307: assert (cus != null);
1308:
1309: // Resolve annotations
1310: Map<Class<? extends java.lang.annotation.Annotation>, java.lang.annotation.Annotation> declaredAnnotations = newAnnotationMap();
1311: if (!resolveAnnotations(logger,
1312: cus.referenceContext.currentPackage.annotations,
1313: declaredAnnotations)) {
1314: // Failed to resolve.
1315: //
1316: return false;
1317: }
1318:
1319: pkg.addAnnotations(declaredAnnotations);
1320:
1321: return true;
1322: }
1323:
1324: private boolean resolveParameter(TreeLogger logger,
1325: JAbstractMethod method, Argument jparam) {
1326: TypeBinding jtype = jparam.binding.type;
1327: JType type = resolveType(logger, jtype);
1328: if (type == null) {
1329: // Unresolved.
1330: //
1331: return false;
1332: }
1333:
1334: // Resolve annotations
1335: Map<Class<? extends java.lang.annotation.Annotation>, java.lang.annotation.Annotation> declaredAnnotations = newAnnotationMap();
1336: if (!resolveAnnotations(logger, jparam.annotations,
1337: declaredAnnotations)) {
1338: // Failed to resolve.
1339: //
1340: return false;
1341: }
1342:
1343: String name = String.valueOf(jparam.name);
1344: new JParameter(method, type, name, declaredAnnotations);
1345: if (jparam.isVarArgs()) {
1346: method.setVarArgs();
1347: }
1348: return true;
1349: }
1350:
1351: private boolean resolveParameters(TreeLogger logger,
1352: JAbstractMethod method, Argument[] jparams) {
1353: if (jparams != null) {
1354: for (int i = 0; i < jparams.length; i++) {
1355: Argument jparam = jparams[i];
1356: if (!resolveParameter(logger, method, jparam)) {
1357: return false;
1358: }
1359: }
1360: }
1361: return true;
1362: }
1363:
1364: private boolean resolveThrownType(TreeLogger logger,
1365: JAbstractMethod method, TypeReference jthrown) {
1366:
1367: JType type = resolveType(logger, jthrown.resolvedType);
1368: if (type == null) {
1369: // Not resolved.
1370: //
1371: return false;
1372: }
1373:
1374: method.addThrows(type);
1375:
1376: return true;
1377: }
1378:
1379: private boolean resolveThrownTypes(TreeLogger logger,
1380: JAbstractMethod method, TypeReference[] jthrows) {
1381: if (jthrows != null) {
1382: for (int i = 0; i < jthrows.length; i++) {
1383: TypeReference jthrown = jthrows[i];
1384: if (!resolveThrownType(logger, method, jthrown)) {
1385: return false;
1386: }
1387: }
1388: }
1389: return true;
1390: }
1391:
1392: private JType resolveType(TreeLogger logger, TypeBinding binding) {
1393: TypeOracle oracle = cacheManager.getTypeOracle();
1394: // Check for primitives.
1395: //
1396: if (binding instanceof BaseTypeBinding) {
1397: switch (binding.id) {
1398: case TypeIds.T_boolean:
1399: return JPrimitiveType.BOOLEAN;
1400: case TypeIds.T_byte:
1401: return JPrimitiveType.BYTE;
1402: case TypeIds.T_char:
1403: return JPrimitiveType.CHAR;
1404: case TypeIds.T_short:
1405: return JPrimitiveType.SHORT;
1406: case TypeIds.T_int:
1407: return JPrimitiveType.INT;
1408: case TypeIds.T_long:
1409: return JPrimitiveType.LONG;
1410: case TypeIds.T_float:
1411: return JPrimitiveType.FLOAT;
1412: case TypeIds.T_double:
1413: return JPrimitiveType.DOUBLE;
1414: case TypeIds.T_void:
1415: return JPrimitiveType.VOID;
1416: default:
1417: assert false : "Unexpected base type id " + binding.id;
1418: }
1419: }
1420:
1421: /*
1422: * Check for a user-defined type, which may be either a SourceTypeBinding or
1423: * a RawTypeBinding. Both of these are subclasses of ReferenceBinding and
1424: * all the functionality we need is on ReferenceBinding, so we cast it to
1425: * that and deal with them in common code.
1426: *
1427: * TODO: do we need to do anything more with raw types?
1428: */
1429: if (binding instanceof SourceTypeBinding
1430: || binding instanceof RawTypeBinding) {
1431: ReferenceBinding referenceBinding = (ReferenceBinding) binding;
1432:
1433: // First check the type oracle to prefer type identity with the type
1434: // oracle we're assimilating into.
1435: //
1436: String typeName = getQualifiedName(referenceBinding);
1437: JType resolvedType = oracle.findType(typeName);
1438: if (resolvedType == null) {
1439: // Otherwise, it should be something we've mapped during this build.
1440: resolvedType = cacheManager
1441: .getTypeForBinding(referenceBinding);
1442: }
1443:
1444: if (resolvedType != null) {
1445: if (binding instanceof RawTypeBinding) {
1446: // Use the raw type instead of the generic type.
1447: JGenericType genericType = (JGenericType) resolvedType;
1448: resolvedType = genericType.getRawType();
1449: }
1450: return resolvedType;
1451: }
1452: }
1453:
1454: // Check for an array.
1455: //
1456: if (binding instanceof ArrayBinding) {
1457: ArrayBinding arrayBinding = (ArrayBinding) binding;
1458:
1459: // Start by resolving the leaf type.
1460: //
1461: TypeBinding leafBinding = arrayBinding.leafComponentType;
1462: JType resolvedType = resolveType(logger, leafBinding);
1463: if (resolvedType != null) {
1464: int dims = arrayBinding.dimensions;
1465: for (int i = 0; i < dims; ++i) {
1466: // By using the oracle to intern, we guarantee correct identity
1467: // mapping of lazily-created array types.
1468: //
1469: resolvedType = oracle.getArrayType(resolvedType);
1470: }
1471: return resolvedType;
1472: } else {
1473: // Fall-through to failure.
1474: //
1475: }
1476: }
1477:
1478: // Check for parameterized.
1479: if (binding instanceof ParameterizedTypeBinding) {
1480: ParameterizedTypeBinding ptBinding = (ParameterizedTypeBinding) binding;
1481:
1482: /*
1483: * NOTE: it is possible for ParameterizedTypeBinding.arguments to be null.
1484: * This can happen if a generic class has a non-static, non-generic, inner
1485: * class that references a TypeParameter from its enclosing generic type.
1486: * You would think that typeVariables() would do the right thing but it
1487: * does not.
1488: */
1489: TypeBinding[] arguments = ptBinding.arguments;
1490: int nArguments = arguments != null ? arguments.length : 0;
1491: JClassType[] typeArguments = new JClassType[nArguments];
1492: boolean failed = false;
1493: for (int i = 0; i < typeArguments.length; ++i) {
1494: typeArguments[i] = (JClassType) resolveType(logger,
1495: arguments[i]);
1496: if (typeArguments[i] == null) {
1497: failed = true;
1498: }
1499: }
1500:
1501: JClassType enclosingType = null;
1502: if (ptBinding.enclosingType() != null) {
1503: enclosingType = (JClassType) resolveType(logger,
1504: ptBinding.enclosingType());
1505: if (enclosingType == null) {
1506: failed = true;
1507: }
1508: }
1509:
1510: /*
1511: * NOTE: In the case where a generic type has a nested, non-static,
1512: * non-generic type. The type for the binding will not be a generic type.
1513: */
1514: JType resolveType = resolveType(logger, ptBinding.type);
1515: if (resolveType == null) {
1516: failed = true;
1517: }
1518:
1519: if (!failed) {
1520: if (resolveType.isGenericType() != null) {
1521: return oracle.getParameterizedType(resolveType
1522: .isGenericType(), enclosingType,
1523: typeArguments);
1524: } else {
1525: /*
1526: * A static type (enum or class) that does not declare any type
1527: * parameters that is nested within a generic type might be referenced
1528: * via a parameterized type by JDT. In this case we just return the
1529: * type and don't treat it as a parameterized.
1530: */
1531: return resolveType;
1532: }
1533: } else {
1534: // Fall-through to failure
1535: }
1536: }
1537:
1538: if (binding instanceof TypeVariableBinding) {
1539: TypeVariableBinding tvBinding = (TypeVariableBinding) binding;
1540: JTypeParameter typeParameter = (JTypeParameter) cacheManager
1541: .getTypeForBinding(tvBinding);
1542: assert (typeParameter != null);
1543: return typeParameter;
1544: }
1545:
1546: if (binding instanceof WildcardBinding) {
1547: WildcardBinding wcBinding = (WildcardBinding) binding;
1548:
1549: assert (wcBinding.otherBounds == null);
1550:
1551: JBound bounds;
1552: switch (wcBinding.boundKind) {
1553: case Wildcard.EXTENDS: {
1554: assert (wcBinding.bound != null);
1555: JClassType upperBound = (JClassType) resolveType(
1556: logger, wcBinding.bound);
1557: bounds = new JUpperBound(
1558: new JClassType[] { upperBound });
1559: }
1560: break;
1561: case Wildcard.SUPER: {
1562: assert (wcBinding.bound != null);
1563: JClassType lowerBound = (JClassType) resolveType(
1564: logger, wcBinding.bound);
1565: bounds = new JLowerBound(
1566: new JClassType[] { lowerBound });
1567: }
1568: break;
1569: case Wildcard.UNBOUND: {
1570: JClassType upperBound = (JClassType) resolveType(
1571: logger, wcBinding.erasure());
1572: bounds = new JUpperBound(
1573: new JClassType[] { upperBound });
1574: assert (bounds.getFirstBound().getQualifiedSourceName()
1575: .equals("java.lang.Object"));
1576: }
1577: break;
1578: default:
1579: assert false : "WildcardBinding of unknown boundKind???";
1580: return null;
1581: }
1582:
1583: return oracle.getWildcardType(bounds);
1584: }
1585:
1586: // Log other cases we know about that don't make sense.
1587: //
1588: if (binding instanceof BinaryTypeBinding) {
1589: logger
1590: .log(
1591: TreeLogger.WARN,
1592: "Source not available for this type, so it cannot be resolved",
1593: null);
1594: }
1595:
1596: String name = String.valueOf(binding.readableName());
1597: logger.log(TreeLogger.WARN, "Unable to resolve type: " + name
1598: + " binding: " + binding.getClass().getCanonicalName(),
1599: null);
1600: return null;
1601: }
1602:
1603: private boolean resolveTypeDeclaration(TreeLogger logger,
1604: char[] unitSource, TypeDeclaration jclass) {
1605: SourceTypeBinding binding = jclass.binding;
1606: if (binding.constantPoolName() == null) {
1607: /*
1608: * Weird case: if JDT determines that this local class is totally
1609: * uninstantiable, it won't bother allocating a local name.
1610: */
1611: return true;
1612: }
1613:
1614: String qname = String.valueOf(binding.qualifiedSourceName());
1615: logger.log(TreeLogger.SPAM, "Found type '" + qname + "'", null);
1616:
1617: // Handle package-info classes.
1618: if (isPackageInfoTypeName(qname)) {
1619: return resolvePackage(logger, jclass);
1620: }
1621:
1622: // Just resolve the type.
1623: JRealClassType type = (JRealClassType) resolveType(logger,
1624: binding);
1625: if (type == null) {
1626: // Failed to resolve.
1627: //
1628: return false;
1629: }
1630:
1631: // Add modifiers.
1632: //
1633: type.addModifierBits(Shared
1634: .bindingToModifierBits(jclass.binding));
1635:
1636: // Resolve annotations
1637: Map<Class<? extends java.lang.annotation.Annotation>, java.lang.annotation.Annotation> declaredAnnotations = newAnnotationMap();
1638: if (!resolveAnnotations(logger, jclass.annotations,
1639: declaredAnnotations)) {
1640: // Failed to resolve.
1641: //
1642: return false;
1643: }
1644: type.addAnnotations(declaredAnnotations);
1645:
1646: // Resolve type parameters for generic types
1647: JGenericType genericType = type.isGenericType();
1648: if (genericType != null
1649: && !resolveTypeParameters(logger, type.isGenericType(),
1650: jclass.typeParameters)) {
1651: // Failed to resolve
1652: return false;
1653: }
1654:
1655: // Resolve superclass (for classes only).
1656: //
1657: if (type.isInterface() == null) {
1658: ReferenceBinding super classRef = binding.super class;
1659: if (super classRef != null) {
1660: JClassType super class = (JClassType) resolveType(
1661: logger, super classRef);
1662: if (super class == null) {
1663: return false;
1664: }
1665: type.setSuperclass(super class);
1666: }
1667: }
1668:
1669: // Resolve superinterfaces.
1670: //
1671: ReferenceBinding[] super intfRefs = binding.super Interfaces;
1672: for (int i = 0; i < super intfRefs.length; i++) {
1673: ReferenceBinding super intfRef = super intfRefs[i];
1674: JClassType intf = (JClassType) resolveType(logger,
1675: super intfRef);
1676: if (intf == null) {
1677: // Failed to resolve.
1678: //
1679: return false;
1680: }
1681: type.addImplementedInterface(intf);
1682: }
1683:
1684: // Resolve fields.
1685: //
1686: FieldDeclaration[] jfields = jclass.fields;
1687: if (!resolveFields(logger, unitSource, type, jfields)) {
1688: return false;
1689: }
1690:
1691: // Resolve methods.
1692: //
1693: AbstractMethodDeclaration[] jmethods = jclass.methods;
1694: if (!resolveMethods(logger, unitSource, type, jmethods)) {
1695: return false;
1696: }
1697:
1698: // Get tags.
1699: //
1700: if (jclass.javadoc != null) {
1701: if (!parseMetaDataTags(unitSource, type, jclass.javadoc)) {
1702: return false;
1703: }
1704: }
1705:
1706: return true;
1707: }
1708:
1709: private boolean resolveTypeParameter(TreeLogger logger,
1710: JAbstractMethod method, TypeParameter jtypeParameter,
1711: int ordinal) {
1712: JTypeParameter typeParameter = new JTypeParameter(String
1713: .valueOf(jtypeParameter.name), method, ordinal);
1714: return resolveTypeParameter(logger, jtypeParameter.binding,
1715: typeParameter);
1716: }
1717:
1718: private boolean resolveTypeParameter(TreeLogger logger,
1719: JGenericType genericType, TypeParameter jtypeParameter,
1720: int ordinal) {
1721: JTypeParameter typeParameter = new JTypeParameter(String
1722: .valueOf(jtypeParameter.name), genericType, ordinal);
1723: return resolveTypeParameter(logger, jtypeParameter.binding,
1724: typeParameter);
1725: }
1726:
1727: private boolean resolveTypeParameter(TreeLogger logger,
1728: TypeVariableBinding binding, JTypeParameter typeParameter) {
1729: Mapper identityMapper = cacheManager.getIdentityMapper();
1730: assert (identityMapper.get(binding) == null);
1731:
1732: identityMapper.put(binding, typeParameter);
1733:
1734: JBound bounds = createTypeVariableBounds(logger, binding);
1735: typeParameter.setBounds(bounds);
1736:
1737: return true;
1738: }
1739:
1740: private boolean resolveTypeParameters(TreeLogger logger,
1741: JAbstractMethod method, TypeParameter[] jtypeParameters) {
1742: if (jtypeParameters != null) {
1743: for (int i = 0; i < jtypeParameters.length; ++i) {
1744: if (!resolveTypeParameter(logger, method,
1745: jtypeParameters[i], i)) {
1746: return false;
1747: }
1748: }
1749: }
1750: return true;
1751: }
1752:
1753: private boolean resolveTypeParameters(TreeLogger logger,
1754: JGenericType genericType, TypeParameter[] jtypeParameters) {
1755: if (jtypeParameters != null) {
1756: for (int i = 0; i < jtypeParameters.length; ++i) {
1757: if (!resolveTypeParameter(logger, genericType,
1758: jtypeParameters[i], i)) {
1759: return false;
1760: }
1761: }
1762: }
1763: return true;
1764: }
1765: }
|