0001: /*******************************************************************************
0002: * Copyright (c) 2000, 2007 IBM Corporation and others.
0003: * All rights reserved. This program and the accompanying materials
0004: * are made available under the terms of the Eclipse Public License v1.0
0005: * which accompanies this distribution, and is available at
0006: * http://www.eclipse.org/legal/epl-v10.html
0007: *
0008: * Contributors:
0009: * IBM Corporation - initial API and implementation
0010: *******************************************************************************/package org.eclipse.jdt.internal.corext.refactoring;
0011:
0012: import java.util.ArrayList;
0013: import java.util.Collections;
0014: import java.util.HashMap;
0015: import java.util.Iterator;
0016: import java.util.List;
0017: import java.util.Map;
0018:
0019: import org.eclipse.core.runtime.Assert;
0020: import org.eclipse.core.runtime.CoreException;
0021: import org.eclipse.core.runtime.IProgressMonitor;
0022: import org.eclipse.core.runtime.NullProgressMonitor;
0023:
0024: import org.eclipse.ltk.core.refactoring.RefactoringStatus;
0025:
0026: import org.eclipse.jdt.core.Flags;
0027: import org.eclipse.jdt.core.ICompilationUnit;
0028: import org.eclipse.jdt.core.IJavaProject;
0029: import org.eclipse.jdt.core.IMethod;
0030: import org.eclipse.jdt.core.IPackageFragment;
0031: import org.eclipse.jdt.core.ISourceRange;
0032: import org.eclipse.jdt.core.IType;
0033: import org.eclipse.jdt.core.ITypeParameter;
0034: import org.eclipse.jdt.core.JavaModelException;
0035: import org.eclipse.jdt.core.WorkingCopyOwner;
0036: import org.eclipse.jdt.core.compiler.IProblem;
0037: import org.eclipse.jdt.core.dom.AST;
0038: import org.eclipse.jdt.core.dom.ASTNode;
0039: import org.eclipse.jdt.core.dom.ASTParser;
0040: import org.eclipse.jdt.core.dom.AbstractTypeDeclaration;
0041: import org.eclipse.jdt.core.dom.AnnotationTypeDeclaration;
0042: import org.eclipse.jdt.core.dom.AnnotationTypeMemberDeclaration;
0043: import org.eclipse.jdt.core.dom.AnonymousClassDeclaration;
0044: import org.eclipse.jdt.core.dom.ArrayType;
0045: import org.eclipse.jdt.core.dom.Block;
0046: import org.eclipse.jdt.core.dom.BodyDeclaration;
0047: import org.eclipse.jdt.core.dom.ClassInstanceCreation;
0048: import org.eclipse.jdt.core.dom.CompilationUnit;
0049: import org.eclipse.jdt.core.dom.EnumDeclaration;
0050: import org.eclipse.jdt.core.dom.IExtendedModifier;
0051: import org.eclipse.jdt.core.dom.ITypeBinding;
0052: import org.eclipse.jdt.core.dom.ImportDeclaration;
0053: import org.eclipse.jdt.core.dom.MethodDeclaration;
0054: import org.eclipse.jdt.core.dom.Modifier;
0055: import org.eclipse.jdt.core.dom.Name;
0056: import org.eclipse.jdt.core.dom.PackageDeclaration;
0057: import org.eclipse.jdt.core.dom.PrimitiveType;
0058: import org.eclipse.jdt.core.dom.QualifiedName;
0059: import org.eclipse.jdt.core.dom.QualifiedType;
0060: import org.eclipse.jdt.core.dom.SimpleName;
0061: import org.eclipse.jdt.core.dom.SingleVariableDeclaration;
0062: import org.eclipse.jdt.core.dom.Type;
0063: import org.eclipse.jdt.core.dom.TypeDeclaration;
0064: import org.eclipse.jdt.core.dom.TypeParameter;
0065: import org.eclipse.jdt.core.search.IJavaSearchConstants;
0066: import org.eclipse.jdt.core.search.IJavaSearchScope;
0067: import org.eclipse.jdt.core.search.SearchEngine;
0068: import org.eclipse.jdt.core.search.SearchPattern;
0069: import org.eclipse.jdt.core.search.TypeNameMatch;
0070:
0071: import org.eclipse.jdt.internal.corext.dom.ASTFlattener;
0072: import org.eclipse.jdt.internal.corext.dom.ASTNodes;
0073: import org.eclipse.jdt.internal.corext.dom.HierarchicalASTVisitor;
0074: import org.eclipse.jdt.internal.corext.dom.NodeFinder;
0075: import org.eclipse.jdt.internal.corext.dom.Selection;
0076: import org.eclipse.jdt.internal.corext.dom.SelectionAnalyzer;
0077: import org.eclipse.jdt.internal.corext.refactoring.util.RefactoringASTParser;
0078: import org.eclipse.jdt.internal.corext.util.JavaModelUtil;
0079: import org.eclipse.jdt.internal.corext.util.Messages;
0080: import org.eclipse.jdt.internal.corext.util.TypeNameMatchCollector;
0081:
0082: import org.eclipse.jdt.internal.ui.JavaPlugin;
0083: import org.eclipse.jdt.internal.ui.refactoring.contentassist.JavaTypeCompletionProcessor;
0084:
0085: public class TypeContextChecker {
0086: private static class MethodTypesChecker {
0087:
0088: private static final String METHOD_NAME = "__$$__"; //$NON-NLS-1$
0089:
0090: private final IMethod fMethod;
0091: private final StubTypeContext fStubTypeContext;
0092: private final List/*<ParameterInfo>*/fParameterInfos;
0093: private final ReturnTypeInfo fReturnTypeInfo;
0094:
0095: public MethodTypesChecker(IMethod method,
0096: StubTypeContext stubTypeContext,
0097: List/*<ParameterInfo>*/parameterInfos,
0098: ReturnTypeInfo returnTypeInfo) {
0099: fMethod = method;
0100: fStubTypeContext = stubTypeContext;
0101: fParameterInfos = parameterInfos;
0102: fReturnTypeInfo = returnTypeInfo;
0103: }
0104:
0105: public RefactoringStatus[] checkAndResolveMethodTypes()
0106: throws CoreException {
0107: RefactoringStatus[] results = new MethodTypesSyntaxChecker(
0108: fMethod, fParameterInfos, fReturnTypeInfo)
0109: .checkSyntax();
0110: for (int i = 0; i < results.length; i++)
0111: if (results[i] != null && results[i].hasFatalError())
0112: return results;
0113:
0114: int parameterCount = fParameterInfos.size();
0115: String[] types = new String[parameterCount + 1];
0116: for (int i = 0; i < parameterCount; i++)
0117: types[i] = ParameterInfo
0118: .stripEllipsis(((ParameterInfo) fParameterInfos
0119: .get(i)).getNewTypeName());
0120: types[parameterCount] = fReturnTypeInfo.getNewTypeName();
0121: RefactoringStatus[] semanticsResults = new RefactoringStatus[parameterCount + 1];
0122: ITypeBinding[] typeBindings = resolveBindings(types,
0123: semanticsResults, true);
0124:
0125: boolean needsSecondPass = false;
0126: for (int i = 0; i < types.length; i++)
0127: if (typeBindings[i] == null
0128: || !semanticsResults[i].isOK())
0129: needsSecondPass = true;
0130:
0131: RefactoringStatus[] semanticsResults2 = new RefactoringStatus[parameterCount + 1];
0132: if (needsSecondPass)
0133: typeBindings = resolveBindings(types,
0134: semanticsResults2, false);
0135:
0136: for (int i = 0; i < fParameterInfos.size(); i++) {
0137: ParameterInfo parameterInfo = (ParameterInfo) fParameterInfos
0138: .get(i);
0139: if (!parameterInfo.isResolve())
0140: continue;
0141: if (parameterInfo.getOldTypeBinding() != null
0142: && !parameterInfo.isTypeNameChanged()) {
0143: parameterInfo.setNewTypeBinding(parameterInfo
0144: .getOldTypeBinding());
0145: } else {
0146: parameterInfo.setNewTypeBinding(typeBindings[i]);
0147: if (typeBindings[i] == null
0148: || (needsSecondPass && !semanticsResults2[i]
0149: .isOK())) {
0150: if (results[i] == null)
0151: results[i] = semanticsResults2[i];
0152: else
0153: results[i].merge(semanticsResults2[i]);
0154: }
0155: }
0156: }
0157: fReturnTypeInfo
0158: .setNewTypeBinding(typeBindings[fParameterInfos
0159: .size()]);
0160: if (typeBindings[parameterCount] == null
0161: || (needsSecondPass && !semanticsResults2[parameterCount]
0162: .isOK())) {
0163: if (results[parameterCount] == null)
0164: results[parameterCount] = semanticsResults2[parameterCount];
0165: else
0166: results[parameterCount]
0167: .merge(semanticsResults2[parameterCount]);
0168: }
0169:
0170: return results;
0171: }
0172:
0173: private ITypeBinding[] resolveBindings(String[] types,
0174: RefactoringStatus[] results, boolean firstPass)
0175: throws CoreException {
0176: //TODO: split types into parameterTypes and returnType
0177: int parameterCount = types.length - 1;
0178: ITypeBinding[] typeBindings = new ITypeBinding[types.length];
0179:
0180: StringBuffer cuString = new StringBuffer();
0181: cuString.append(fStubTypeContext.getBeforeString());
0182: int offsetBeforeMethodName = appendMethodDeclaration(
0183: cuString, types, parameterCount);
0184: cuString.append(fStubTypeContext.getAfterString());
0185:
0186: // need a working copy to tell the parser where to resolve (package visible) types
0187: ICompilationUnit wc = fMethod.getCompilationUnit()
0188: .getWorkingCopy(new WorkingCopyOwner() {/*subclass*/
0189: }, new NullProgressMonitor());
0190: try {
0191: wc.getBuffer().setContents(cuString.toString());
0192: CompilationUnit compilationUnit = new RefactoringASTParser(
0193: AST.JLS3).parse(wc, true);
0194: ASTNode method = NodeFinder.perform(compilationUnit,
0195: offsetBeforeMethodName, METHOD_NAME.length())
0196: .getParent();
0197: Type[] typeNodes = new Type[types.length];
0198: if (method instanceof MethodDeclaration) {
0199: MethodDeclaration methodDeclaration = (MethodDeclaration) method;
0200: typeNodes[parameterCount] = methodDeclaration
0201: .getReturnType2();
0202: List/*<SingleVariableDeclaration>*/parameters = methodDeclaration
0203: .parameters();
0204: for (int i = 0; i < parameterCount; i++)
0205: typeNodes[i] = ((SingleVariableDeclaration) parameters
0206: .get(i)).getType();
0207:
0208: } else if (method instanceof AnnotationTypeMemberDeclaration) {
0209: typeNodes[0] = ((AnnotationTypeMemberDeclaration) method)
0210: .getType();
0211: }
0212:
0213: for (int i = 0; i < types.length; i++) {
0214: Type type = typeNodes[i];
0215: if (type == null) {
0216: String msg = Messages
0217: .format(
0218: RefactoringCoreMessages.TypeContextChecker_couldNotResolveType,
0219: types[i]);
0220: results[i] = RefactoringStatus
0221: .createErrorStatus(msg);
0222: continue;
0223: }
0224: results[i] = new RefactoringStatus();
0225: IProblem[] problems = ASTNodes.getProblems(type,
0226: ASTNodes.NODE_ONLY, ASTNodes.PROBLEMS);
0227: if (problems.length > 0) {
0228: for (int p = 0; p < problems.length; p++)
0229: if (isError(problems[p], type))
0230: results[i].addError(problems[p]
0231: .getMessage());
0232: }
0233: typeBindings[i] = type.resolveBinding();
0234: typeBindings[i] = handleBug84585(typeBindings[i]);
0235: if (firstPass && typeBindings[i] == null)
0236: types[i] = qualifyTypes(type, results[i]);
0237: }
0238: return typeBindings;
0239: } finally {
0240: wc.discardWorkingCopy();
0241: }
0242: }
0243:
0244: /**
0245: * Decides if a problem matters.
0246: * @param problem the problem
0247: * @param type the current type
0248: * @return return if a problem matters.
0249: */
0250: private boolean isError(IProblem problem, Type type) {
0251: return true;
0252: }
0253:
0254: private int appendMethodDeclaration(StringBuffer cuString,
0255: String[] types, int parameterCount)
0256: throws JavaModelException {
0257: if (Flags.isStatic(fMethod.getFlags()))
0258: cuString.append("static "); //$NON-NLS-1$
0259:
0260: ITypeParameter[] methodTypeParameters = fMethod
0261: .getTypeParameters();
0262: if (methodTypeParameters.length != 0) {
0263: cuString.append('<');
0264: for (int i = 0; i < methodTypeParameters.length; i++) {
0265: ITypeParameter typeParameter = methodTypeParameters[i];
0266: if (i > 0)
0267: cuString.append(',');
0268: cuString.append(typeParameter.getElementName());
0269: }
0270: cuString.append("> "); //$NON-NLS-1$
0271: }
0272:
0273: cuString.append(types[parameterCount]).append(' ');
0274: int offsetBeforeMethodName = cuString.length();
0275: cuString.append(METHOD_NAME).append('(');
0276: for (int i = 0; i < parameterCount; i++) {
0277: if (i > 0)
0278: cuString.append(',');
0279: cuString.append(types[i]).append(" p").append(i); //$NON-NLS-1$
0280: }
0281: cuString.append(");"); //$NON-NLS-1$
0282:
0283: return offsetBeforeMethodName;
0284: }
0285:
0286: private String qualifyTypes(Type type,
0287: final RefactoringStatus result) throws CoreException {
0288: class NestedException extends RuntimeException {
0289: private static final long serialVersionUID = 1L;
0290:
0291: NestedException(CoreException e) {
0292: super (e);
0293: }
0294: }
0295: ASTFlattener flattener = new ASTFlattener() {
0296: public boolean visit(SimpleName node) {
0297: appendResolved(node.getIdentifier());
0298: return false;
0299: }
0300:
0301: public boolean visit(QualifiedName node) {
0302: appendResolved(node.getFullyQualifiedName());
0303: return false;
0304: }
0305:
0306: public boolean visit(QualifiedType node) {
0307: appendResolved(ASTNodes.asString(node));
0308: return false;
0309: }
0310:
0311: private void appendResolved(String typeName) {
0312: String resolvedType;
0313: try {
0314: resolvedType = resolveType(typeName, result,
0315: fMethod.getDeclaringType(), null);
0316: } catch (CoreException e) {
0317: throw new NestedException(e);
0318: }
0319: this .fBuffer.append(resolvedType);
0320: }
0321: };
0322: try {
0323: type.accept(flattener);
0324: } catch (NestedException e) {
0325: throw ((CoreException) e.getCause());
0326: }
0327: return flattener.getResult();
0328: }
0329:
0330: private static String resolveType(String elementTypeName,
0331: RefactoringStatus status, IType declaringType,
0332: IProgressMonitor pm) throws CoreException {
0333: String[][] fqns = declaringType
0334: .resolveType(elementTypeName);
0335: if (fqns != null) {
0336: if (fqns.length == 1) {
0337: return JavaModelUtil.concatenateName(fqns[0][0],
0338: fqns[0][1]);
0339: } else if (fqns.length > 1) {
0340: String[] keys = { elementTypeName,
0341: String.valueOf(fqns.length) };
0342: String msg = Messages
0343: .format(
0344: RefactoringCoreMessages.TypeContextChecker_ambiguous,
0345: keys);
0346: status.addError(msg);
0347: return elementTypeName;
0348: }
0349: }
0350:
0351: List typeRefsFound = findTypeInfos(elementTypeName,
0352: declaringType, pm);
0353: if (typeRefsFound.size() == 0) {
0354: String[] keys = { elementTypeName };
0355: String msg = Messages
0356: .format(
0357: RefactoringCoreMessages.TypeContextChecker_not_unique,
0358: keys);
0359: status.addError(msg);
0360: return elementTypeName;
0361: } else if (typeRefsFound.size() == 1) {
0362: TypeNameMatch typeInfo = (TypeNameMatch) typeRefsFound
0363: .get(0);
0364: return typeInfo.getFullyQualifiedName();
0365: } else {
0366: Assert.isTrue(typeRefsFound.size() > 1);
0367: String[] keys = { elementTypeName,
0368: String.valueOf(typeRefsFound.size()) };
0369: String msg = Messages
0370: .format(
0371: RefactoringCoreMessages.TypeContextChecker_ambiguous,
0372: keys);
0373: status.addError(msg);
0374: return elementTypeName;
0375: }
0376: }
0377:
0378: private static List findTypeInfos(String typeName,
0379: IType contextType, IProgressMonitor pm)
0380: throws JavaModelException {
0381: IJavaSearchScope scope = SearchEngine
0382: .createJavaSearchScope(
0383: new IJavaProject[] { contextType
0384: .getJavaProject() }, true);
0385: IPackageFragment currPackage = contextType
0386: .getPackageFragment();
0387: ArrayList collectedInfos = new ArrayList();
0388: TypeNameMatchCollector requestor = new TypeNameMatchCollector(
0389: collectedInfos);
0390: int matchMode = SearchPattern.R_EXACT_MATCH
0391: | SearchPattern.R_CASE_SENSITIVE;
0392: new SearchEngine()
0393: .searchAllTypeNames(
0394: null,
0395: matchMode,
0396: typeName.toCharArray(),
0397: matchMode,
0398: IJavaSearchConstants.TYPE,
0399: scope,
0400: requestor,
0401: IJavaSearchConstants.WAIT_UNTIL_READY_TO_SEARCH,
0402: pm);
0403:
0404: List result = new ArrayList();
0405: for (Iterator iter = collectedInfos.iterator(); iter
0406: .hasNext();) {
0407: TypeNameMatch curr = (TypeNameMatch) iter.next();
0408: IType type = curr.getType();
0409: if (type != null) {
0410: boolean visible = true;
0411: try {
0412: visible = JavaModelUtil.isVisible(type,
0413: currPackage);
0414: } catch (JavaModelException e) {
0415: //Assume visibile if not available
0416: }
0417: if (visible) {
0418: result.add(curr);
0419: }
0420: }
0421: }
0422: return result;
0423: }
0424:
0425: }
0426:
0427: private static class MethodTypesSyntaxChecker {
0428:
0429: private final IMethod fMethod;
0430: private final List/*<ParameterInfo>*/fParameterInfos;
0431: private final ReturnTypeInfo fReturnTypeInfo;
0432:
0433: public MethodTypesSyntaxChecker(IMethod method,
0434: List/*<ParameterInfo>*/parameterInfos,
0435: ReturnTypeInfo returnTypeInfo) {
0436: fMethod = method;
0437: fParameterInfos = parameterInfos;
0438: fReturnTypeInfo = returnTypeInfo;
0439: }
0440:
0441: public RefactoringStatus[] checkSyntax() {
0442: int parameterCount = fParameterInfos.size();
0443: RefactoringStatus[] results = new RefactoringStatus[parameterCount + 1];
0444: results[parameterCount] = checkReturnTypeSyntax();
0445: for (int i = 0; i < parameterCount; i++) {
0446: ParameterInfo info = (ParameterInfo) fParameterInfos
0447: .get(i);
0448: if (!info.isDeleted())
0449: results[i] = checkParameterTypeSyntax(info);
0450: }
0451: return results;
0452: }
0453:
0454: private RefactoringStatus checkParameterTypeSyntax(
0455: ParameterInfo info) {
0456: if (!info.isAdded() && !info.isTypeNameChanged()
0457: && !info.isDeleted())
0458: return null;
0459: return TypeContextChecker.checkParameterTypeSyntax(info
0460: .getNewTypeName(), fMethod.getJavaProject());
0461: }
0462:
0463: private RefactoringStatus checkReturnTypeSyntax() {
0464: String newTypeName = fReturnTypeInfo.getNewTypeName();
0465: if ("".equals(newTypeName.trim())) { //$NON-NLS-1$
0466: String msg = RefactoringCoreMessages.TypeContextChecker_return_type_not_empty;
0467: return RefactoringStatus.createFatalErrorStatus(msg);
0468: }
0469: List problemsCollector = new ArrayList(0);
0470: Type parsedType = parseType(newTypeName, fMethod
0471: .getJavaProject(), problemsCollector);
0472: if (parsedType == null) {
0473: String msg = Messages
0474: .format(
0475: RefactoringCoreMessages.TypeContextChecker_invalid_return_type,
0476: new String[] { newTypeName });
0477: return RefactoringStatus.createFatalErrorStatus(msg);
0478: }
0479: if (problemsCollector.size() == 0)
0480: return null;
0481:
0482: RefactoringStatus result = new RefactoringStatus();
0483: for (Iterator iter = problemsCollector.iterator(); iter
0484: .hasNext();) {
0485: String msg = Messages
0486: .format(
0487: RefactoringCoreMessages.TypeContextChecker_invalid_return_type_syntax,
0488: new String[] { newTypeName,
0489: (String) iter.next() });
0490: result.addError(msg);
0491: }
0492: return result;
0493: }
0494:
0495: private static boolean isVoidArrayType(Type type) {
0496: if (!type.isArrayType())
0497: return false;
0498:
0499: ArrayType arrayType = (ArrayType) type;
0500: if (!arrayType.getComponentType().isPrimitiveType())
0501: return false;
0502: PrimitiveType primitiveType = (PrimitiveType) arrayType
0503: .getComponentType();
0504: return (primitiveType.getPrimitiveTypeCode() == PrimitiveType.VOID);
0505: }
0506:
0507: }
0508:
0509: private static Type parseType(String typeString,
0510: IJavaProject javaProject,
0511: List/*<IProblem>*/problemsCollector) {
0512: if ("".equals(typeString.trim())) //speed up for a common case //$NON-NLS-1$
0513: return null;
0514: if (!typeString.trim().equals(typeString))
0515: return null;
0516:
0517: StringBuffer cuBuff = new StringBuffer();
0518: cuBuff.append("interface A{"); //$NON-NLS-1$
0519: int offset = cuBuff.length();
0520: cuBuff.append(typeString).append(" m();}"); //$NON-NLS-1$
0521:
0522: ASTParser p = ASTParser.newParser(AST.JLS3);
0523: p.setSource(cuBuff.toString().toCharArray());
0524: p.setProject(javaProject);
0525: CompilationUnit cu = (CompilationUnit) p.createAST(null);
0526: Selection selection = Selection.createFromStartLength(offset,
0527: typeString.length());
0528: SelectionAnalyzer analyzer = new SelectionAnalyzer(selection,
0529: false);
0530: cu.accept(analyzer);
0531: ASTNode selected = analyzer.getFirstSelectedNode();
0532: if (!(selected instanceof Type))
0533: return null;
0534: Type type = (Type) selected;
0535: if (MethodTypesSyntaxChecker.isVoidArrayType(type))
0536: return null;
0537: IProblem[] problems = ASTNodes.getProblems(type,
0538: ASTNodes.NODE_ONLY, ASTNodes.PROBLEMS);
0539: if (problems.length > 0) {
0540: for (int i = 0; i < problems.length; i++)
0541: problemsCollector.add(problems[i].getMessage());
0542: }
0543:
0544: String typeNodeRange = cuBuff
0545: .substring(type.getStartPosition(), ASTNodes
0546: .getExclusiveEnd(type));
0547: if (typeString.equals(typeNodeRange))
0548: return type;
0549: else
0550: return null;
0551: }
0552:
0553: private static ITypeBinding handleBug84585(ITypeBinding typeBinding) {
0554: if (typeBinding == null)
0555: return null;
0556: else if (typeBinding.isGenericType()
0557: && !typeBinding.isRawType()
0558: && !typeBinding.isParameterizedType())
0559: return null; //see bug 84585
0560: else
0561: return typeBinding;
0562: }
0563:
0564: public static RefactoringStatus[] checkAndResolveMethodTypes(
0565: IMethod method, StubTypeContext stubTypeContext,
0566: List parameterInfos, ReturnTypeInfo returnTypeInfo)
0567: throws CoreException {
0568: MethodTypesChecker checker = new MethodTypesChecker(method,
0569: stubTypeContext, parameterInfos, returnTypeInfo);
0570: return checker.checkAndResolveMethodTypes();
0571: }
0572:
0573: public static RefactoringStatus[] checkMethodTypesSyntax(
0574: IMethod method, List parameterInfos,
0575: ReturnTypeInfo returnTypeInfo) {
0576: MethodTypesSyntaxChecker checker = new MethodTypesSyntaxChecker(
0577: method, parameterInfos, returnTypeInfo);
0578: return checker.checkSyntax();
0579: }
0580:
0581: public static RefactoringStatus checkParameterTypeSyntax(
0582: String type, IJavaProject project) {
0583: String newTypeName = ParameterInfo.stripEllipsis(type.trim())
0584: .trim();
0585:
0586: if ("".equals(newTypeName.trim())) { //$NON-NLS-1$
0587: String msg = Messages
0588: .format(
0589: RefactoringCoreMessages.TypeContextChecker_parameter_type,
0590: new String[] { type });
0591: return RefactoringStatus.createFatalErrorStatus(msg);
0592: }
0593:
0594: if (ParameterInfo.isVarargs(type)
0595: && !JavaModelUtil.is50OrHigher(project)) {
0596: String msg = Messages
0597: .format(
0598: RefactoringCoreMessages.TypeContextChecker_no_vararg_below_50,
0599: new String[] { type });
0600: return RefactoringStatus.createFatalErrorStatus(msg);
0601: }
0602:
0603: List problemsCollector = new ArrayList(0);
0604: Type parsedType = parseType(newTypeName, project,
0605: problemsCollector);
0606: boolean valid = parsedType != null;
0607: if (valid && parsedType instanceof PrimitiveType)
0608: valid = !PrimitiveType.VOID
0609: .equals(((PrimitiveType) parsedType)
0610: .getPrimitiveTypeCode());
0611: if (!valid) {
0612: String msg = Messages
0613: .format(
0614: RefactoringCoreMessages.TypeContextChecker_invalid_type_name,
0615: new String[] { newTypeName });
0616: return RefactoringStatus.createFatalErrorStatus(msg);
0617: }
0618: if (problemsCollector.size() == 0)
0619: return null;
0620:
0621: RefactoringStatus result = new RefactoringStatus();
0622: for (Iterator iter = problemsCollector.iterator(); iter
0623: .hasNext();) {
0624: String msg = Messages
0625: .format(
0626: RefactoringCoreMessages.TypeContextChecker_invalid_type_syntax,
0627: new String[] { newTypeName,
0628: (String) iter.next() });
0629: result.addError(msg);
0630: }
0631: return result;
0632: }
0633:
0634: public static StubTypeContext createStubTypeContext(
0635: ICompilationUnit cu, CompilationUnit root, int focalPosition)
0636: throws CoreException {
0637: StringBuffer bufBefore = new StringBuffer();
0638: StringBuffer bufAfter = new StringBuffer();
0639:
0640: int introEnd = 0;
0641: PackageDeclaration pack = root.getPackage();
0642: if (pack != null)
0643: introEnd = pack.getStartPosition() + pack.getLength();
0644: List imports = root.imports();
0645: if (imports.size() > 0) {
0646: ImportDeclaration lastImport = (ImportDeclaration) imports
0647: .get(imports.size() - 1);
0648: introEnd = lastImport.getStartPosition()
0649: + lastImport.getLength();
0650: }
0651: bufBefore.append(cu.getBuffer().getText(0, introEnd));
0652:
0653: fillWithTypeStubs(bufBefore, bufAfter, focalPosition, root
0654: .types());
0655: bufBefore.append(' ');
0656: bufAfter.insert(0, ' ');
0657: return new StubTypeContext(cu, bufBefore.toString(), bufAfter
0658: .toString());
0659: }
0660:
0661: private static void fillWithTypeStubs(final StringBuffer bufBefore,
0662: final StringBuffer bufAfter, final int focalPosition,
0663: List/*<? extends BodyDeclaration>*/types) {
0664: StringBuffer buf;
0665: for (Iterator iter = types.iterator(); iter.hasNext();) {
0666: BodyDeclaration bodyDeclaration = (BodyDeclaration) iter
0667: .next();
0668: if (!(bodyDeclaration instanceof AbstractTypeDeclaration)) {
0669: //account for local classes:
0670: if (!(bodyDeclaration instanceof MethodDeclaration))
0671: continue;
0672: int bodyStart = bodyDeclaration.getStartPosition();
0673: int bodyEnd = bodyDeclaration.getStartPosition()
0674: + bodyDeclaration.getLength();
0675: if (!(bodyStart < focalPosition && focalPosition < bodyEnd))
0676: continue;
0677: MethodDeclaration methodDeclaration = (MethodDeclaration) bodyDeclaration;
0678: buf = bufBefore;
0679: appendModifiers(buf, methodDeclaration.modifiers());
0680: appendTypeParameters(buf, methodDeclaration
0681: .typeParameters());
0682: buf.append(" void "); //$NON-NLS-1$
0683: buf.append(methodDeclaration.getName().getIdentifier());
0684: buf.append("(){\n"); //$NON-NLS-1$
0685: Block body = methodDeclaration.getBody();
0686: body.accept(new HierarchicalASTVisitor() {
0687: public boolean visit(AbstractTypeDeclaration node) {
0688: fillWithTypeStubs(bufBefore, bufAfter,
0689: focalPosition, Collections
0690: .singletonList(node));
0691: return false;
0692: }
0693:
0694: public boolean visit(ClassInstanceCreation node) {
0695: AnonymousClassDeclaration anonDecl = node
0696: .getAnonymousClassDeclaration();
0697: if (anonDecl == null)
0698: return false;
0699: int anonStart = anonDecl.getStartPosition();
0700: int anonEnd = anonDecl.getStartPosition()
0701: + anonDecl.getLength();
0702: if (!(anonStart < focalPosition && focalPosition < anonEnd))
0703: return false;
0704: bufBefore.append(" new "); //$NON-NLS-1$
0705: bufBefore.append(node.getType().toString());
0706: bufBefore.append("(){\n"); //$NON-NLS-1$
0707: fillWithTypeStubs(bufBefore, bufAfter,
0708: focalPosition, anonDecl
0709: .bodyDeclarations());
0710: bufAfter.append("};\n"); //$NON-NLS-1$
0711: return false;
0712: }
0713: });
0714: buf = bufAfter;
0715: buf.append("}\n"); //$NON-NLS-1$
0716: continue;
0717: }
0718:
0719: AbstractTypeDeclaration decl = (AbstractTypeDeclaration) bodyDeclaration;
0720: buf = decl.getStartPosition() < focalPosition ? bufBefore
0721: : bufAfter;
0722: appendModifiers(buf, decl.modifiers());
0723:
0724: if (decl instanceof TypeDeclaration) {
0725: TypeDeclaration type = (TypeDeclaration) decl;
0726: buf
0727: .append(type.isInterface() ? "interface " : "class "); //$NON-NLS-1$//$NON-NLS-2$
0728: buf.append(type.getName().getIdentifier());
0729: appendTypeParameters(buf, type.typeParameters());
0730: if (type.getSuperclassType() != null) {
0731: buf.append(" extends "); //$NON-NLS-1$
0732: buf.append(ASTNodes.asString(type
0733: .getSuperclassType()));
0734: }
0735: List super Interfaces = type.super InterfaceTypes();
0736: appendSuperInterfaces(buf, super Interfaces);
0737:
0738: } else if (decl instanceof AnnotationTypeDeclaration) {
0739: AnnotationTypeDeclaration annotation = (AnnotationTypeDeclaration) decl;
0740: buf.append("@interface "); //$NON-NLS-1$
0741: buf.append(annotation.getName().getIdentifier());
0742:
0743: } else if (decl instanceof EnumDeclaration) {
0744: EnumDeclaration enumDecl = (EnumDeclaration) decl;
0745: buf.append("enum "); //$NON-NLS-1$
0746: buf.append(enumDecl.getName().getIdentifier());
0747: List super Interfaces = enumDecl.super InterfaceTypes();
0748: appendSuperInterfaces(buf, super Interfaces);
0749: }
0750:
0751: buf.append("{\n"); //$NON-NLS-1$
0752: if (decl instanceof EnumDeclaration)
0753: buf.append(";\n"); //$NON-NLS-1$
0754: fillWithTypeStubs(bufBefore, bufAfter, focalPosition, decl
0755: .bodyDeclarations());
0756: buf = decl.getStartPosition() + decl.getLength() < focalPosition ? bufBefore
0757: : bufAfter;
0758: buf.append("}\n"); //$NON-NLS-1$
0759: }
0760: }
0761:
0762: private static void appendTypeParameters(StringBuffer buf,
0763: List typeParameters) {
0764: int typeParametersCount = typeParameters.size();
0765: if (typeParametersCount > 0) {
0766: buf.append('<');
0767: for (int i = 0; i < typeParametersCount; i++) {
0768: TypeParameter typeParameter = (TypeParameter) typeParameters
0769: .get(i);
0770: buf.append(ASTNodes.asString(typeParameter));
0771: if (i < typeParametersCount - 1)
0772: buf.append(',');
0773: }
0774: }
0775: }
0776:
0777: private static void appendModifiers(StringBuffer buf, List modifiers) {
0778: for (Iterator iterator = modifiers.iterator(); iterator
0779: .hasNext();) {
0780: IExtendedModifier extendedModifier = (IExtendedModifier) iterator
0781: .next();
0782: if (extendedModifier.isModifier()) {
0783: Modifier modifier = (Modifier) extendedModifier;
0784: buf.append(modifier.getKeyword().toString())
0785: .append(' ');
0786: }
0787: }
0788: }
0789:
0790: private static void appendSuperInterfaces(StringBuffer buf,
0791: List super Interfaces) {
0792: int super InterfaceCount = super Interfaces.size();
0793: if (super InterfaceCount > 0) {
0794: buf.append(" implements "); //$NON-NLS-1$
0795: for (int i = 0; i < super InterfaceCount; i++) {
0796: Type super Interface = (Type) super Interfaces.get(i);
0797: buf.append(ASTNodes.asString(super Interface));
0798: if (i < super InterfaceCount - 1)
0799: buf.append(',');
0800: }
0801: }
0802: }
0803:
0804: public static StubTypeContext createSuperInterfaceStubTypeContext(
0805: String typeName, IType enclosingType,
0806: IPackageFragment packageFragment) {
0807: return createSupertypeStubTypeContext(typeName, true,
0808: enclosingType, packageFragment);
0809: }
0810:
0811: public static StubTypeContext createSuperClassStubTypeContext(
0812: String typeName, IType enclosingType,
0813: IPackageFragment packageFragment) {
0814: return createSupertypeStubTypeContext(typeName, false,
0815: enclosingType, packageFragment);
0816: }
0817:
0818: private static StubTypeContext createSupertypeStubTypeContext(
0819: String typeName, boolean isInterface, IType enclosingType,
0820: IPackageFragment packageFragment) {
0821: StubTypeContext stubTypeContext;
0822: String prolog = "class " + typeName + (isInterface ? " implements " : " extends "); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
0823: String epilog = " {} "; //$NON-NLS-1$
0824: if (enclosingType != null) {
0825: try {
0826: ICompilationUnit cu = enclosingType
0827: .getCompilationUnit();
0828: ISourceRange typeSourceRange = enclosingType
0829: .getSourceRange();
0830: int focalPosition = typeSourceRange.getOffset()
0831: + typeSourceRange.getLength() - 1; // before closing brace
0832:
0833: ASTParser parser = ASTParser.newParser(AST.JLS3);
0834: parser.setSource(cu);
0835: parser.setFocalPosition(focalPosition);
0836: CompilationUnit compilationUnit = (CompilationUnit) parser
0837: .createAST(null);
0838:
0839: stubTypeContext = createStubTypeContext(cu,
0840: compilationUnit, focalPosition);
0841: stubTypeContext = new StubTypeContext(stubTypeContext
0842: .getCuHandle(), stubTypeContext
0843: .getBeforeString()
0844: + prolog, epilog
0845: + stubTypeContext.getAfterString());
0846: } catch (CoreException e) {
0847: JavaPlugin.log(e);
0848: stubTypeContext = new StubTypeContext(null, null, null);
0849: }
0850:
0851: } else if (packageFragment != null) {
0852: ICompilationUnit cu = packageFragment
0853: .getCompilationUnit(JavaTypeCompletionProcessor.DUMMY_CU_NAME);
0854: stubTypeContext = new StubTypeContext(
0855: cu,
0856: "package " + packageFragment.getElementName() + ";" + prolog, epilog); //$NON-NLS-1$//$NON-NLS-2$
0857:
0858: } else {
0859: stubTypeContext = new StubTypeContext(null, null, null);
0860: }
0861: return stubTypeContext;
0862: }
0863:
0864: public static Type parseSuperClass(String super Class) {
0865: return parseSuperType(super Class, false);
0866: }
0867:
0868: public static Type parseSuperInterface(String super Interface) {
0869: return parseSuperType(super Interface, true);
0870: }
0871:
0872: private static Type parseSuperType(String super Type,
0873: boolean isInterface) {
0874: if (!super Type.trim().equals(super Type)) {
0875: return null;
0876: }
0877:
0878: StringBuffer cuBuff = new StringBuffer();
0879: if (isInterface)
0880: cuBuff.append("class __X__ implements "); //$NON-NLS-1$
0881: else
0882: cuBuff.append("class __X__ extends "); //$NON-NLS-1$
0883: int offset = cuBuff.length();
0884: cuBuff.append(super Type).append(" {}"); //$NON-NLS-1$
0885:
0886: ASTParser p = ASTParser.newParser(AST.JLS3);
0887: p.setSource(cuBuff.toString().toCharArray());
0888: Map options = new HashMap();
0889: JavaModelUtil.set50CompilanceOptions(options);
0890: p.setCompilerOptions(options);
0891: CompilationUnit cu = (CompilationUnit) p.createAST(null);
0892: ASTNode selected = NodeFinder.perform(cu, offset, super Type
0893: .length());
0894: if (selected instanceof Name)
0895: selected = selected.getParent();
0896: if (selected.getStartPosition() != offset
0897: || selected.getLength() != super Type.length()
0898: || !(selected instanceof Type)
0899: || selected instanceof PrimitiveType) {
0900: return null;
0901: }
0902: Type type = (Type) selected;
0903:
0904: String typeNodeRange = cuBuff
0905: .substring(type.getStartPosition(), ASTNodes
0906: .getExclusiveEnd(type));
0907: if (!super Type.equals(typeNodeRange)) {
0908: return null;
0909: }
0910: return type;
0911: }
0912:
0913: public static ITypeBinding resolveSuperClass(String super class,
0914: IType typeHandle, StubTypeContext super ClassContext) {
0915: StringBuffer cuString = new StringBuffer();
0916: cuString.append(super ClassContext.getBeforeString());
0917: cuString.append(super class);
0918: cuString.append(super ClassContext.getAfterString());
0919:
0920: try {
0921: ICompilationUnit wc = typeHandle.getCompilationUnit()
0922: .getWorkingCopy(new WorkingCopyOwner() {/*subclass*/
0923: }, new NullProgressMonitor());
0924: try {
0925: wc.getBuffer().setContents(cuString.toString());
0926: CompilationUnit compilationUnit = new RefactoringASTParser(
0927: AST.JLS3).parse(wc, true);
0928: ASTNode type = NodeFinder.perform(compilationUnit,
0929: super ClassContext.getBeforeString().length(),
0930: super class.length());
0931: if (type instanceof Type) {
0932: return handleBug84585(((Type) type)
0933: .resolveBinding());
0934: } else if (type instanceof Name) {
0935: ASTNode parent = type.getParent();
0936: if (parent instanceof Type)
0937: return handleBug84585(((Type) parent)
0938: .resolveBinding());
0939: }
0940: throw new IllegalStateException();
0941: } finally {
0942: wc.discardWorkingCopy();
0943: }
0944: } catch (JavaModelException e) {
0945: return null;
0946: }
0947: }
0948:
0949: public static ITypeBinding[] resolveSuperInterfaces(
0950: String[] interfaces, IType typeHandle,
0951: StubTypeContext super InterfaceContext) {
0952: ITypeBinding[] result = new ITypeBinding[interfaces.length];
0953:
0954: int[] interfaceOffsets = new int[interfaces.length];
0955: StringBuffer cuString = new StringBuffer();
0956: cuString.append(super InterfaceContext.getBeforeString());
0957: int last = interfaces.length - 1;
0958: for (int i = 0; i <= last; i++) {
0959: interfaceOffsets[i] = cuString.length();
0960: cuString.append(interfaces[i]);
0961: if (i != last)
0962: cuString.append(", "); //$NON-NLS-1$
0963: }
0964: cuString.append(super InterfaceContext.getAfterString());
0965:
0966: try {
0967: ICompilationUnit wc = typeHandle.getCompilationUnit()
0968: .getWorkingCopy(new WorkingCopyOwner() {/*subclass*/
0969: }, new NullProgressMonitor());
0970: try {
0971: wc.getBuffer().setContents(cuString.toString());
0972: CompilationUnit compilationUnit = new RefactoringASTParser(
0973: AST.JLS3).parse(wc, true);
0974: for (int i = 0; i <= last; i++) {
0975: ASTNode type = NodeFinder
0976: .perform(compilationUnit,
0977: interfaceOffsets[i], interfaces[i]
0978: .length());
0979: if (type instanceof Type) {
0980: result[i] = handleBug84585(((Type) type)
0981: .resolveBinding());
0982: } else if (type instanceof Name) {
0983: ASTNode parent = type.getParent();
0984: if (parent instanceof Type) {
0985: result[i] = handleBug84585(((Type) parent)
0986: .resolveBinding());
0987: } else {
0988: throw new IllegalStateException();
0989: }
0990: } else {
0991: throw new IllegalStateException();
0992: }
0993: }
0994: } finally {
0995: wc.discardWorkingCopy();
0996: }
0997: } catch (JavaModelException e) {
0998: // won't happen
0999: }
1000: return result;
1001: }
1002: }
|