Copyright (c) 2018, 2019 IBM Corporation and others. This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 which accompanies this distribution, and is available at https://www.eclipse.org/legal/epl-2.0/ SPDX-License-Identifier: EPL-2.0 Contributors: IBM Corporation - initial API and implementation Red Hat Inc. - copied and pared down to methods needed by jdt.core.manipulation Microsoft Corporation - copied methods needed by jdt.core.manipulation
/******************************************************************************* * Copyright (c) 2018, 2019 IBM Corporation and others. * * This program and the accompanying materials * are made available under the terms of the Eclipse Public License 2.0 * which accompanies this distribution, and is available at * https://www.eclipse.org/legal/epl-2.0/ * * SPDX-License-Identifier: EPL-2.0 * * Contributors: * IBM Corporation - initial API and implementation * Red Hat Inc. - copied and pared down to methods needed by jdt.core.manipulation * Microsoft Corporation - copied methods needed by jdt.core.manipulation *******************************************************************************/
package org.eclipse.jdt.internal.corext.codemanipulation; import java.util.ArrayList; import java.util.Arrays; import java.util.EnumSet; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import org.eclipse.core.runtime.Assert; import org.eclipse.core.runtime.CoreException; import org.eclipse.text.edits.TextEditGroup; import org.eclipse.jdt.core.Flags; import org.eclipse.jdt.core.ICompilationUnit; import org.eclipse.jdt.core.IJavaElement; import org.eclipse.jdt.core.IJavaProject; import org.eclipse.jdt.core.IMember; import org.eclipse.jdt.core.IMethod; import org.eclipse.jdt.core.ISourceRange; import org.eclipse.jdt.core.JavaCore; import org.eclipse.jdt.core.JavaModelException; import org.eclipse.jdt.core.dom.AST; import org.eclipse.jdt.core.dom.ASTNode; import org.eclipse.jdt.core.dom.ASTParser; import org.eclipse.jdt.core.dom.Annotation; import org.eclipse.jdt.core.dom.ArrayType; import org.eclipse.jdt.core.dom.Assignment; import org.eclipse.jdt.core.dom.Block; import org.eclipse.jdt.core.dom.CompilationUnit; import org.eclipse.jdt.core.dom.Dimension; import org.eclipse.jdt.core.dom.Expression; import org.eclipse.jdt.core.dom.FieldAccess; import org.eclipse.jdt.core.dom.IAnnotationBinding; import org.eclipse.jdt.core.dom.IExtendedModifier; import org.eclipse.jdt.core.dom.IMethodBinding; import org.eclipse.jdt.core.dom.IPackageBinding; import org.eclipse.jdt.core.dom.ITypeBinding; import org.eclipse.jdt.core.dom.IVariableBinding; import org.eclipse.jdt.core.dom.Javadoc; import org.eclipse.jdt.core.dom.MethodDeclaration; import org.eclipse.jdt.core.dom.MethodInvocation; import org.eclipse.jdt.core.dom.Modifier; import org.eclipse.jdt.core.dom.Name; import org.eclipse.jdt.core.dom.NodeFinder; import org.eclipse.jdt.core.dom.PrimitiveType; import org.eclipse.jdt.core.dom.ReturnStatement; import org.eclipse.jdt.core.dom.SingleVariableDeclaration; import org.eclipse.jdt.core.dom.Statement; import org.eclipse.jdt.core.dom.SuperConstructorInvocation; import org.eclipse.jdt.core.dom.SuperMethodInvocation; import org.eclipse.jdt.core.dom.Type; import org.eclipse.jdt.core.dom.TypeParameter; import org.eclipse.jdt.core.dom.rewrite.ASTRewrite; import org.eclipse.jdt.core.dom.rewrite.ImportRewrite; import org.eclipse.jdt.core.dom.rewrite.ImportRewrite.ImportRewriteContext; import org.eclipse.jdt.core.dom.rewrite.ImportRewrite.TypeLocation; import org.eclipse.jdt.core.dom.rewrite.ListRewrite; import org.eclipse.jdt.core.manipulation.CodeGeneration; import org.eclipse.jdt.internal.core.manipulation.StubUtility; import org.eclipse.jdt.internal.corext.codemanipulation.AddDelegateMethodsOperation.DelegateEntry; import org.eclipse.jdt.internal.corext.dom.ASTNodeFactory; import org.eclipse.jdt.internal.corext.dom.ASTNodes; import org.eclipse.jdt.internal.corext.dom.Bindings; import org.eclipse.jdt.internal.corext.dom.IASTSharedValues; import org.eclipse.jdt.internal.corext.refactoring.util.JavaElementUtil; import org.eclipse.jdt.internal.corext.util.JavaModelUtil;
Utilities for code generation based on AST rewrite.
Since:1.10
/** * Utilities for code generation based on AST rewrite. * * @since 1.10 */
public final class StubUtility2Core { /* This method should work with all AST levels. */ public static MethodDeclaration createConstructorStub(ICompilationUnit unit, ASTRewrite rewrite, ImportRewrite imports, ImportRewriteContext context, IMethodBinding binding, String type, int modifiers, boolean omitSuperForDefConst, boolean todo, CodeGenerationSettings settings, Map<String, String> formatSettings) throws CoreException { AST ast= rewrite.getAST(); MethodDeclaration decl= ast.newMethodDeclaration(); decl.modifiers().addAll(ASTNodeFactory.newModifiers(ast, modifiers & ~Modifier.ABSTRACT & ~Modifier.NATIVE)); decl.setName(ast.newSimpleName(type)); decl.setConstructor(true); StubUtility2Core.createTypeParameters(imports, context, ast, binding, decl); List<SingleVariableDeclaration> parameters= StubUtility2Core.createParameters(unit.getJavaProject(), imports, context, ast, binding, null, decl); StubUtility2Core.createThrownExceptions(decl, binding, imports, context, ast); Block body= ast.newBlock(); decl.setBody(body); String delimiter= StubUtility.getLineDelimiterUsed(unit); String bodyStatement= ""; //$NON-NLS-1$ if (!omitSuperForDefConst || !parameters.isEmpty()) { SuperConstructorInvocation invocation= ast.newSuperConstructorInvocation(); SingleVariableDeclaration varDecl= null; for (Iterator<SingleVariableDeclaration> iterator= parameters.iterator(); iterator.hasNext();) { varDecl= iterator.next(); invocation.arguments().add(ast.newSimpleName(varDecl.getName().getIdentifier())); } bodyStatement= ASTNodes.asFormattedString(invocation, 0, delimiter, formatSettings == null ? unit.getJavaProject().getOptions(true) : formatSettings); } if (todo) { String placeHolder= CodeGeneration.getMethodBodyContent(unit, type, binding.getName(), true, bodyStatement, delimiter); if (placeHolder != null) { ReturnStatement todoNode= (ReturnStatement) rewrite.createStringPlaceholder(placeHolder, ASTNode.RETURN_STATEMENT); body.statements().add(todoNode); } } else { ReturnStatement statementNode= (ReturnStatement) rewrite.createStringPlaceholder(bodyStatement, ASTNode.RETURN_STATEMENT); body.statements().add(statementNode); } if (settings != null && settings.createComments) { String string= CodeGeneration.getMethodComment(unit, type, decl, binding, delimiter); if (string != null) { Javadoc javadoc= (Javadoc) rewrite.createStringPlaceholder(string, ASTNode.JAVADOC); decl.setJavadoc(javadoc); } } return decl; } public static MethodDeclaration createConstructorStub(ICompilationUnit unit, ASTRewrite rewrite, ImportRewrite imports, ImportRewriteContext context, ITypeBinding typeBinding, IMethodBinding superConstructor, IVariableBinding[] variableBindings, int modifiers, CodeGenerationSettings settings) throws CoreException { AST ast= rewrite.getAST(); MethodDeclaration decl= ast.newMethodDeclaration(); decl.modifiers().addAll(ASTNodeFactory.newModifiers(ast, modifiers & ~Modifier.ABSTRACT & ~Modifier.NATIVE)); decl.setName(ast.newSimpleName(typeBinding.getName())); decl.setConstructor(true); List<SingleVariableDeclaration> parameters= decl.parameters(); if (superConstructor != null) { createTypeParameters(imports, context, ast, superConstructor, decl); createParameters(unit.getJavaProject(), imports, context, ast, superConstructor, null, decl); createThrownExceptions(decl, superConstructor, imports, context, ast); } Block body= ast.newBlock(); decl.setBody(body); String delimiter= StubUtility.getLineDelimiterUsed(unit); if (superConstructor != null) { SuperConstructorInvocation invocation= ast.newSuperConstructorInvocation(); SingleVariableDeclaration varDecl= null; for (Iterator<SingleVariableDeclaration> iterator= parameters.iterator(); iterator.hasNext();) { varDecl= iterator.next(); invocation.arguments().add(ast.newSimpleName(varDecl.getName().getIdentifier())); } body.statements().add(invocation); } List<String> prohibited= new ArrayList<>(); for (final Iterator<SingleVariableDeclaration> iterator= parameters.iterator(); iterator.hasNext();) prohibited.add(iterator.next().getName().getIdentifier()); String param= null; List<String> list= new ArrayList<>(prohibited); String[] excluded= null; for (int i= 0; i < variableBindings.length; i++) { SingleVariableDeclaration var= ast.newSingleVariableDeclaration(); var.setType(imports.addImport(variableBindings[i].getType(), ast, context, TypeLocation.PARAMETER)); excluded= new String[list.size()]; list.toArray(excluded); param= suggestParameterName(unit, variableBindings[i], excluded); list.add(param); var.setName(ast.newSimpleName(param)); parameters.add(var); } list= new ArrayList<>(prohibited); for (int i= 0; i < variableBindings.length; i++) { excluded= new String[list.size()]; list.toArray(excluded); final String paramName= suggestParameterName(unit, variableBindings[i], excluded); list.add(paramName); final String fieldName= variableBindings[i].getName(); Expression expression= null; if (paramName.equals(fieldName) || settings.useKeywordThis) { FieldAccess access= ast.newFieldAccess(); access.setExpression(ast.newThisExpression()); access.setName(ast.newSimpleName(fieldName)); expression= access; } else expression= ast.newSimpleName(fieldName); Assignment assignment= ast.newAssignment(); assignment.setLeftHandSide(expression); assignment.setRightHandSide(ast.newSimpleName(paramName)); assignment.setOperator(Assignment.Operator.ASSIGN); body.statements().add(ast.newExpressionStatement(assignment)); } if (settings != null && settings.createComments) { String string= CodeGeneration.getMethodComment(unit, typeBinding.getName(), decl, superConstructor, delimiter); if (string != null) { Javadoc javadoc= (Javadoc) rewrite.createStringPlaceholder(string, ASTNode.JAVADOC); decl.setJavadoc(javadoc); } } return decl; } public static MethodDeclaration createDelegationStub(ICompilationUnit unit, ASTRewrite rewrite, ImportRewrite imports, ImportRewriteContext context, IMethodBinding delegate, IVariableBinding delegatingField, CodeGenerationSettings settings) throws CoreException { Assert.isNotNull(delegate); Assert.isNotNull(delegatingField); Assert.isNotNull(settings); AST ast= rewrite.getAST(); MethodDeclaration decl= ast.newMethodDeclaration(); decl.modifiers().addAll(ASTNodeFactory.newModifiers(ast, delegate.getModifiers() & ~Modifier.DEFAULT & ~Modifier.SYNCHRONIZED & ~Modifier.ABSTRACT & ~Modifier.NATIVE)); decl.setName(ast.newSimpleName(delegate.getName())); decl.setConstructor(false); createTypeParameters(imports, context, ast, delegate, decl); decl.setReturnType2(imports.addImport(delegate.getReturnType(), ast, context, TypeLocation.RETURN_TYPE)); List<SingleVariableDeclaration> params= createParameters(unit.getJavaProject(), imports, context, ast, delegate, null, decl); createThrownExceptions(decl, delegate, imports, context, ast); Block body= ast.newBlock(); decl.setBody(body); String delimiter= StubUtility.getLineDelimiterUsed(unit); Statement statement= null; MethodInvocation invocation= ast.newMethodInvocation(); invocation.setName(ast.newSimpleName(delegate.getName())); List<Expression> arguments= invocation.arguments(); for (int i= 0; i < params.size(); i++) arguments.add(ast.newSimpleName(params.get(i).getName().getIdentifier())); if (settings.useKeywordThis) { FieldAccess access= ast.newFieldAccess(); access.setExpression(ast.newThisExpression()); access.setName(ast.newSimpleName(delegatingField.getName())); invocation.setExpression(access); } else invocation.setExpression(ast.newSimpleName(delegatingField.getName())); if (delegate.getReturnType().isPrimitive() && delegate.getReturnType().getName().equals("void")) {//$NON-NLS-1$ statement= ast.newExpressionStatement(invocation); } else { ReturnStatement returnStatement= ast.newReturnStatement(); returnStatement.setExpression(invocation); statement= returnStatement; } body.statements().add(statement); ITypeBinding declaringType= delegatingField.getDeclaringClass(); if (declaringType == null) { // can be null for return decl; } String qualifiedName= declaringType.getQualifiedName(); IPackageBinding packageBinding= declaringType.getPackage(); if (packageBinding != null) { if (packageBinding.getName().length() > 0 && qualifiedName.startsWith(packageBinding.getName())) qualifiedName= qualifiedName.substring(packageBinding.getName().length()); } if (settings.createComments) { /* * TODO: have API for delegate method comments This is an inlined * version of * {@link CodeGeneration#getMethodComment(ICompilationUnit, String, MethodDeclaration, IMethodBinding, String)} */ delegate= delegate.getMethodDeclaration(); String declaringClassQualifiedName= delegate.getDeclaringClass().getQualifiedName(); String linkToMethodName= delegate.getName(); String[] parameterTypesQualifiedNames= StubUtility.getParameterTypeNamesForSeeTag(delegate); String string= StubUtility.getMethodComment(unit, qualifiedName, decl, delegate.isDeprecated(), linkToMethodName, declaringClassQualifiedName, parameterTypesQualifiedNames, true, delimiter); if (string != null) { Javadoc javadoc= (Javadoc) rewrite.createStringPlaceholder(string, ASTNode.JAVADOC); decl.setJavadoc(javadoc); } } return decl; } public static MethodDeclaration createImplementationStubCore(ICompilationUnit unit, ASTRewrite rewrite, ImportRewrite imports, ImportRewriteContext context, IMethodBinding binding, ITypeBinding targetType, CodeGenerationSettings settings, boolean inInterface, ASTNode astNode, boolean snippetStringSupport) throws CoreException { return createImplementationStubCore(unit, rewrite, imports, context, binding, null, targetType, settings, inInterface, astNode, snippetStringSupport); } public static MethodDeclaration createImplementationStubCore(ICompilationUnit unit, ASTRewrite rewrite, ImportRewrite imports, ImportRewriteContext context, IMethodBinding binding, String[] parameterNames, ITypeBinding targetType, CodeGenerationSettings settings, boolean inInterface, ASTNode astNode, boolean snippetStringSupport) throws CoreException { Assert.isNotNull(imports); Assert.isNotNull(rewrite); AST ast= rewrite.getAST(); String type= Bindings.getTypeQualifiedName(targetType); IJavaProject javaProject= unit.getJavaProject(); EnumSet<TypeLocation> nullnessDefault= null; if (astNode != null && JavaCore.ENABLED.equals(javaProject.getOption(JavaCore.COMPILER_ANNOTATION_NULL_ANALYSIS, true))) { nullnessDefault= RedundantNullnessTypeAnnotationsFilter.determineNonNullByDefaultLocations(astNode, RedundantNullnessTypeAnnotationsFilter.determineNonNullByDefaultNames(javaProject)); } MethodDeclaration decl= ast.newMethodDeclaration(); decl.modifiers().addAll(StubUtility2Core.getImplementationModifiers(ast, binding, inInterface, imports, context, nullnessDefault)); decl.setName(ast.newSimpleName(binding.getName())); decl.setConstructor(false); ITypeBinding bindingReturnType= binding.getReturnType(); bindingReturnType= StubUtility2Core.replaceWildcardsAndCaptures(bindingReturnType); if (JavaModelUtil.is50OrHigher(javaProject)) { StubUtility2Core.createTypeParameters(imports, context, ast, binding, decl); } else { bindingReturnType= bindingReturnType.getErasure(); } decl.setReturnType2(imports.addImport(bindingReturnType, ast, context, TypeLocation.RETURN_TYPE)); List<SingleVariableDeclaration> parameters= StubUtility2Core.createParameters(javaProject, imports, context, ast, binding, parameterNames, decl, nullnessDefault); StubUtility2Core.createThrownExceptions(decl, binding, imports, context, ast); String delimiter= unit.findRecommendedLineSeparator(); int modifiers= binding.getModifiers(); ITypeBinding declaringType= binding.getDeclaringClass(); ITypeBinding typeObject= ast.resolveWellKnownType("java.lang.Object"); //$NON-NLS-1$ if (!inInterface || (declaringType != typeObject && JavaModelUtil.is18OrHigher(javaProject))) { // generate a method body Map<String, String> options= javaProject.getOptions(true); Block body= ast.newBlock(); decl.setBody(body); String bodyStatement= ""; //$NON-NLS-1$ if (Modifier.isAbstract(modifiers)) { Expression expression= ASTNodeFactory.newDefaultExpression(ast, decl.getReturnType2(), decl.getExtraDimensions()); if (expression != null) { ReturnStatement returnStatement= ast.newReturnStatement(); returnStatement.setExpression(expression); bodyStatement= ASTNodes.asFormattedString(returnStatement, 0, delimiter, options); } } else { SuperMethodInvocation invocation= ast.newSuperMethodInvocation(); if (declaringType.isInterface()) { ITypeBinding supertype= Bindings.findImmediateSuperTypeInHierarchy(targetType, declaringType.getTypeDeclaration().getQualifiedName()); if (supertype == null) { // should not happen, but better use the type we have rather than failing supertype= declaringType; } if (supertype.isInterface()) { String qualifier= imports.addImport(supertype.getTypeDeclaration(), context); Name name= ASTNodeFactory.newName(ast, qualifier); invocation.setQualifier(name); } } invocation.setName(ast.newSimpleName(binding.getName())); for (SingleVariableDeclaration varDecl : parameters) { invocation.arguments().add(ast.newSimpleName(varDecl.getName().getIdentifier())); } Expression expression= invocation; Type returnType= decl.getReturnType2(); if (returnType instanceof PrimitiveType && ((PrimitiveType) returnType).getPrimitiveTypeCode().equals(PrimitiveType.VOID)) { bodyStatement= ASTNodes.asFormattedString(ast.newExpressionStatement(expression), 0, delimiter, options); } else { ReturnStatement returnStatement= ast.newReturnStatement(); returnStatement.setExpression(expression); bodyStatement= ASTNodes.asFormattedString(returnStatement, 0, delimiter, options); } } if (bodyStatement != null) { StringBuilder placeHolder= new StringBuilder(); if (snippetStringSupport) { final String ESCAPE_DOLLAR= "\\\\\\$"; //$NON-NLS-1$ final String DOLLAR= "\\$"; //$NON-NLS-1$ bodyStatement= bodyStatement.replaceAll(DOLLAR, ESCAPE_DOLLAR); } String bodyContent= CodeGeneration.getMethodBodyContent(unit, type, binding.getName(), false, bodyStatement, delimiter); if (snippetStringSupport) { placeHolder.append("${0"); //$NON-NLS-1$ if (bodyContent != null) { placeHolder.append(":"); //$NON-NLS-1$ placeHolder.append(bodyContent); } placeHolder.append("}"); //$NON-NLS-1$ } else { if (bodyContent != null) { placeHolder.append(bodyContent); } } if (bodyContent != null || snippetStringSupport) { ReturnStatement todoNode= (ReturnStatement) rewrite.createStringPlaceholder(placeHolder.toString(), ASTNode.RETURN_STATEMENT); body.statements().add(todoNode); } } } if (settings != null && settings.createComments) { String string= CodeGeneration.getMethodComment(unit, type, decl, binding, delimiter); if (string != null) { Javadoc javadoc= (Javadoc) rewrite.createStringPlaceholder(string, ASTNode.JAVADOC); decl.setJavadoc(javadoc); } } // According to JLS8 9.2, an interface doesn't implicitly declare non-public members of Object, // and JLS8 9.6.4.4 doesn't allow @Override for these methods (clone and finalize). boolean skipOverride= inInterface && declaringType == typeObject && !Modifier.isPublic(modifiers); if (!skipOverride) { StubUtility2Core.addOverrideAnnotation(settings, javaProject, rewrite, imports, decl, binding.getDeclaringClass().isInterface(), null); } return decl; } public static void createTypeParameters(ImportRewrite imports, ImportRewriteContext context, AST ast, IMethodBinding binding, MethodDeclaration decl) { ITypeBinding[] typeParams= binding.getTypeParameters(); List<TypeParameter> typeParameters= decl.typeParameters(); for (int i= 0; i < typeParams.length; i++) { ITypeBinding curr= typeParams[i]; TypeParameter newTypeParam= ast.newTypeParameter(); newTypeParam.setName(ast.newSimpleName(curr.getName())); ITypeBinding[] typeBounds= curr.getTypeBounds(); if (typeBounds.length != 1 || !"java.lang.Object".equals(typeBounds[0].getQualifiedName())) {//$NON-NLS-1$ List<Type> newTypeBounds= newTypeParam.typeBounds(); for (int k= 0; k < typeBounds.length; k++) { newTypeBounds.add(imports.addImport(typeBounds[k], ast, context, TypeLocation.TYPE_BOUND)); } } typeParameters.add(newTypeParam); } } public static List<SingleVariableDeclaration> createParameters(IJavaProject project, ImportRewrite imports, ImportRewriteContext context, AST ast, IMethodBinding binding, String[] paramNames, MethodDeclaration decl) { return createParameters(project, imports, context, ast, binding, paramNames, decl, null); } public static List<SingleVariableDeclaration> createParameters(IJavaProject project, ImportRewrite imports, ImportRewriteContext context, AST ast, IMethodBinding binding, String[] paramNames, MethodDeclaration decl, EnumSet<TypeLocation> nullnessDefault) { boolean is50OrHigher= JavaModelUtil.is50OrHigher(project); List<SingleVariableDeclaration> parameters= decl.parameters(); ITypeBinding[] params= binding.getParameterTypes(); if (paramNames == null || paramNames.length < params.length) { paramNames= StubUtility.suggestArgumentNames(project, binding); } for (int i= 0; i < params.length; i++) { SingleVariableDeclaration var= ast.newSingleVariableDeclaration(); ITypeBinding type= params[i]; type=replaceWildcardsAndCaptures(type); if (!is50OrHigher) { type= type.getErasure(); var.setType(imports.addImport(type, ast, context, TypeLocation.PARAMETER)); } else if (binding.isVarargs() && type.isArray() && i == params.length - 1) { var.setVarargs(true); /* * Varargs annotations are special. * Example: * foo(@O Object @A [] @B ... arg) * => @B is not an annotation on the array dimension that constitutes the vararg. * It's the type annotation of the *innermost* array dimension. */ int dimensions= type.getDimensions(); @SuppressWarnings("unchecked") List<Annotation>[] dimensionAnnotations= (List<Annotation>[]) new List<?>[dimensions]; for (int dim= 0; dim < dimensions; dim++) { dimensionAnnotations[dim]= new ArrayList<>(); for (IAnnotationBinding annotation : type.getTypeAnnotations()) { dimensionAnnotations[dim].add(imports.addAnnotation(annotation, ast, context)); } type= type.getComponentType(); } Type elementType= imports.addImport(type, ast, context); if (dimensions == 1) { var.setType(elementType); } else { ArrayType arrayType= ast.newArrayType(elementType, dimensions - 1); List<Dimension> dimensionNodes= arrayType.dimensions(); for (int dim= 0; dim < dimensions - 1; dim++) { // all except the innermost dimension Dimension dimension= dimensionNodes.get(dim); dimension.annotations().addAll(dimensionAnnotations[dim]); } var.setType(arrayType); } List<Annotation> varargTypeAnnotations= dimensionAnnotations[dimensions - 1]; var.varargsAnnotations().addAll(varargTypeAnnotations); } else { var.setType(imports.addImport(type, ast, context, TypeLocation.PARAMETER)); } var.setName(ast.newSimpleName(paramNames[i])); IAnnotationBinding[] annotations= binding.getParameterAnnotations(i); for (IAnnotationBinding annotation : annotations) { if (StubUtility2Core.isCopyOnInheritAnnotation(annotation.getAnnotationType(), project, nullnessDefault, TypeLocation.PARAMETER)) var.modifiers().add(imports.addAnnotation(annotation, ast, context)); } parameters.add(var); } return parameters; } public static void createThrownExceptions(MethodDeclaration decl, IMethodBinding method, ImportRewrite imports, ImportRewriteContext context, AST ast) { ITypeBinding[] excTypes= method.getExceptionTypes(); if (ast.apiLevel() >= AST.JLS8) { List<Type> thrownExceptions= decl.thrownExceptionTypes(); for (int i= 0; i < excTypes.length; i++) { Type excType= imports.addImport(excTypes[i], ast, context, TypeLocation.EXCEPTION); thrownExceptions.add(excType); } } else { List<Name> thrownExceptions= getThrownExceptions(decl); for (int i= 0; i < excTypes.length; i++) { String excTypeName= imports.addImport(excTypes[i], context); thrownExceptions.add(ASTNodeFactory.newName(ast, excTypeName)); } } }
Params:
  • decl – method declaration
Returns:thrown exception names
Deprecated:to avoid deprecation warnings
/** * @param decl method declaration * @return thrown exception names * @deprecated to avoid deprecation warnings */
@Deprecated private static List<Name> getThrownExceptions(MethodDeclaration decl) { return decl.thrownExceptions(); } private static IMethodBinding findMethodBinding(IMethodBinding method, List<IMethodBinding> allMethods) { for (int i= 0; i < allMethods.size(); i++) { IMethodBinding curr= allMethods.get(i); if (Bindings.isSubsignature(method, curr)) { return curr; } } return null; } private static IMethodBinding findOverridingMethod(IMethodBinding method, List<IMethodBinding> allMethods) { for (int i= 0; i < allMethods.size(); i++) { IMethodBinding curr= allMethods.get(i); if (Bindings.areOverriddenMethods(curr, method) || Bindings.isSubsignature(curr, method)) return curr; } return null; } private static void findUnimplementedInterfaceMethods(ITypeBinding typeBinding, HashSet<ITypeBinding> visited, ArrayList<IMethodBinding> allMethods, IPackageBinding currPack, ArrayList<IMethodBinding> toImplement) { if (visited.add(typeBinding)) { IMethodBinding[] typeMethods= typeBinding.getDeclaredMethods(); nextMethod: for (int i= 0; i < typeMethods.length; i++) { IMethodBinding curr= typeMethods[i]; for (Iterator<IMethodBinding> allIter= allMethods.iterator(); allIter.hasNext();) { IMethodBinding oneMethod= allIter.next(); if (Bindings.isSubsignature(oneMethod, curr)) { // We've already seen a method that is a subsignature of curr. if (!Bindings.isSubsignature(curr, oneMethod)) { // oneMethod is a true subsignature of curr; let's go with oneMethod continue nextMethod; } // Subsignatures are equivalent. // Check visibility and return types ('getErasure()' tries to achieve effect of "rename type variables") if (Bindings.isVisibleInHierarchy(oneMethod, currPack) && oneMethod.getReturnType().getErasure().isSubTypeCompatible(curr.getReturnType().getErasure())) { // oneMethod is visible and curr doesn't have a stricter return type; let's go with oneMethod continue nextMethod; } // curr is stricter than oneMethod, so let's remove oneMethod allIter.remove(); toImplement.remove(oneMethod); } else if (Bindings.isSubsignature(curr, oneMethod)) { // curr is a true subsignature of oneMethod. Let's remove oneMethod. allIter.remove(); toImplement.remove(oneMethod); } } int modifiers= curr.getModifiers(); if (!Modifier.isStatic(modifiers)) { allMethods.add(curr); if (Modifier.isAbstract(modifiers)) { toImplement.add(curr); } } } ITypeBinding[] superInterfaces= typeBinding.getInterfaces(); for (int i= 0; i < superInterfaces.length; i++) findUnimplementedInterfaceMethods(superInterfaces[i], visited, allMethods, currPack, toImplement); } } public static List<IExtendedModifier> getImplementationModifiers(AST ast, IMethodBinding method, boolean inInterface, ImportRewrite importRewrite, ImportRewriteContext context, EnumSet<TypeLocation> nullnessDefault) throws JavaModelException { IJavaProject javaProject= importRewrite.getCompilationUnit().getJavaProject(); int modifiers= method.getModifiers(); if (inInterface) { modifiers= modifiers & ~Modifier.PROTECTED & ~Modifier.PUBLIC; if (Modifier.isAbstract(modifiers) && JavaModelUtil.is18OrHigher(javaProject)) { modifiers= modifiers | Modifier.DEFAULT; } } else { modifiers= modifiers & ~Modifier.DEFAULT; } modifiers= modifiers & ~Modifier.ABSTRACT & ~Modifier.NATIVE & ~Modifier.PRIVATE; IAnnotationBinding[] annotations= method.getAnnotations(); if (modifiers != Modifier.NONE && annotations.length > 0) { // need an AST of the source method to preserve order of modifiers IMethod iMethod= (IMethod) method.getJavaElement(); if (iMethod != null && JavaElementUtil.isSourceAvailable(iMethod)) { ASTParser parser= ASTParser.newParser(IASTSharedValues.SHARED_AST_LEVEL); parser.setSource(iMethod.getTypeRoot()); parser.setIgnoreMethodBodies(true); CompilationUnit otherCU= (CompilationUnit) parser.createAST(null); ASTNode otherMethod= NodeFinder.perform(otherCU, iMethod.getSourceRange()); if (otherMethod instanceof MethodDeclaration) { MethodDeclaration otherMD= (MethodDeclaration) otherMethod; ArrayList<IExtendedModifier> result= new ArrayList<>(); List<IExtendedModifier> otherModifiers= otherMD.modifiers(); for (IExtendedModifier otherModifier : otherModifiers) { if (otherModifier instanceof Modifier) { int otherFlag= ((Modifier) otherModifier).getKeyword().toFlagValue(); if ((otherFlag & modifiers) != 0) { modifiers= ~otherFlag & modifiers; result.addAll(ast.newModifiers(otherFlag)); } } else { Annotation otherAnnotation= (Annotation) otherModifier; String n= otherAnnotation.getTypeName().getFullyQualifiedName(); for (IAnnotationBinding annotation : annotations) { ITypeBinding otherAnnotationType= annotation.getAnnotationType(); String qn= otherAnnotationType.getQualifiedName(); if (qn.endsWith(n) && (qn.length() == n.length() || qn.charAt(qn.length() - n.length() - 1) == '.')) { if (StubUtility2Core.isCopyOnInheritAnnotation(otherAnnotationType, javaProject, nullnessDefault, TypeLocation.RETURN_TYPE)) result.add(importRewrite.addAnnotation(annotation, ast, context)); break; } } } } result.addAll(ASTNodeFactory.newModifiers(ast, modifiers)); return result; } } } ArrayList<IExtendedModifier> result= new ArrayList<>(); for (IAnnotationBinding annotation : annotations) { if (StubUtility2Core.isCopyOnInheritAnnotation(annotation.getAnnotationType(), javaProject, nullnessDefault, TypeLocation.RETURN_TYPE)) result.add(importRewrite.addAnnotation(annotation, ast, context)); } result.addAll(ASTNodeFactory.newModifiers(ast, modifiers)); return result; } public static DelegateEntry[] getDelegatableMethods(ITypeBinding binding) { final List<DelegateEntry> tuples= new ArrayList<>(); final List<IMethodBinding> declared= new ArrayList<>(); IMethodBinding[] typeMethods= binding.getDeclaredMethods(); declared.addAll(Arrays.asList(typeMethods)); IVariableBinding[] typeFields= binding.getDeclaredFields(); for (int index= 0; index < typeFields.length; index++) { IVariableBinding fieldBinding= typeFields[index]; if (fieldBinding.isField() && !fieldBinding.isEnumConstant() && !fieldBinding.isSynthetic()) getDelegatableMethods(new ArrayList<>(declared), fieldBinding, fieldBinding.getType(), binding, tuples); } // list of tuple<IVariableBinding, IMethodBinding> return tuples.toArray(new DelegateEntry[tuples.size()]); } private static void getDelegatableMethods(List<IMethodBinding> methods, IVariableBinding fieldBinding, ITypeBinding typeBinding, ITypeBinding binding, List<DelegateEntry> result) { boolean match= false; if (typeBinding.isTypeVariable()) { ITypeBinding[] typeBounds= typeBinding.getTypeBounds(); if (typeBounds.length > 0) { for (int i= 0; i < typeBounds.length; i++) { getDelegatableMethods(methods, fieldBinding, typeBounds[i], binding, result); } } else { ITypeBinding objectBinding= Bindings.findTypeInHierarchy(binding, "java.lang.Object"); //$NON-NLS-1$ if (objectBinding != null) { getDelegatableMethods(methods, fieldBinding, objectBinding, binding, result); } } } else { IMethodBinding[] candidates= getDelegateCandidates(typeBinding, binding); for (int index= 0; index < candidates.length; index++) { match= false; final IMethodBinding methodBinding= candidates[index]; for (int offset= 0; offset < methods.size() && !match; offset++) { if (Bindings.areOverriddenMethods(methods.get(offset), methodBinding)) match= true; } if (!match) { result.add(new DelegateEntry(methodBinding, fieldBinding)); methods.add(methodBinding); } } final ITypeBinding superclass= typeBinding.getSuperclass(); if (superclass != null) getDelegatableMethods(methods, fieldBinding, superclass, binding, result); ITypeBinding[] superInterfaces= typeBinding.getInterfaces(); for (int offset= 0; offset < superInterfaces.length; offset++) getDelegatableMethods(methods, fieldBinding, superInterfaces[offset], binding, result); } } private static IMethodBinding[] getDelegateCandidates(ITypeBinding binding, ITypeBinding hierarchy) { List<IMethodBinding> allMethods= new ArrayList<>(); boolean isInterface= binding.isInterface(); IMethodBinding[] typeMethods= binding.getDeclaredMethods(); for (int index= 0; index < typeMethods.length; index++) { final int modifiers= typeMethods[index].getModifiers(); if (!typeMethods[index].isConstructor() && !Modifier.isStatic(modifiers) && (isInterface || Modifier.isPublic(modifiers))) { IMethodBinding result= Bindings.findOverriddenMethodInHierarchy(hierarchy, typeMethods[index]); if (result != null && Flags.isFinal(result.getModifiers())) continue; ITypeBinding[] parameterBindings= typeMethods[index].getParameterTypes(); boolean upper= false; for (int offset= 0; offset < parameterBindings.length; offset++) { if (parameterBindings[offset].isWildcardType() && parameterBindings[offset].isUpperbound()) upper= true; } if (!upper) allMethods.add(typeMethods[index]); } } return allMethods.toArray(new IMethodBinding[allMethods.size()]); } public static IMethodBinding[] getOverridableMethods(AST ast, ITypeBinding typeBinding, boolean isSubType) { List<IMethodBinding> allMethods= new ArrayList<>(); IMethodBinding[] typeMethods= typeBinding.getDeclaredMethods(); for (int index= 0; index < typeMethods.length; index++) { final int modifiers= typeMethods[index].getModifiers(); if (!typeMethods[index].isConstructor() && !Modifier.isStatic(modifiers) && !Modifier.isPrivate(modifiers)) allMethods.add(typeMethods[index]); } ITypeBinding clazz= typeBinding.getSuperclass(); while (clazz != null) { IMethodBinding[] methods= clazz.getDeclaredMethods(); for (int offset= 0; offset < methods.length; offset++) { final int modifiers= methods[offset].getModifiers(); if (!methods[offset].isConstructor() && !Modifier.isStatic(modifiers) && !Modifier.isPrivate(modifiers)) { if (findOverridingMethod(methods[offset], allMethods) == null) allMethods.add(methods[offset]); } } clazz= clazz.getSuperclass(); } clazz= typeBinding; while (clazz != null) { ITypeBinding[] superInterfaces= clazz.getInterfaces(); for (int index= 0; index < superInterfaces.length; index++) { getOverridableMethods(ast, superInterfaces[index], allMethods); } clazz= clazz.getSuperclass(); } if (typeBinding.isInterface()) getOverridableMethods(ast, ast.resolveWellKnownType("java.lang.Object"), allMethods); //$NON-NLS-1$ if (!isSubType) allMethods.removeAll(Arrays.asList(typeMethods)); for (int index= allMethods.size() - 1; index >= 0; index--) { IMethodBinding method= allMethods.get(index); if (Modifier.isFinal(method.getModifiers())) allMethods.remove(index); } return allMethods.toArray(new IMethodBinding[allMethods.size()]); } private static void getOverridableMethods(AST ast, ITypeBinding superBinding, List<IMethodBinding> allMethods) { IMethodBinding[] methods= superBinding.getDeclaredMethods(); for (int offset= 0; offset < methods.length; offset++) { final int modifiers= methods[offset].getModifiers(); if (!methods[offset].isConstructor() && !Modifier.isStatic(modifiers) && !Modifier.isPrivate(modifiers)) { if (findOverridingMethod(methods[offset], allMethods) == null) allMethods.add(methods[offset]); } } ITypeBinding[] superInterfaces= superBinding.getInterfaces(); for (int index= 0; index < superInterfaces.length; index++) { getOverridableMethods(ast, superInterfaces[index], allMethods); } } private static String suggestParameterName(ICompilationUnit unit, IVariableBinding binding, String[] excluded) { String name= StubUtility.getBaseName(binding, unit.getJavaProject()); return StubUtility.suggestArgumentName(unit.getJavaProject(), name, excluded); } public static IMethodBinding[] getUnimplementedMethods(ITypeBinding typeBinding) { return getUnimplementedMethods(typeBinding, false); } public static IMethodBinding[] getUnimplementedMethods(ITypeBinding typeBinding, boolean implementAbstractsOfInput) { ArrayList<IMethodBinding> allMethods= new ArrayList<>(); ArrayList<IMethodBinding> toImplement= new ArrayList<>(); IMethodBinding[] typeMethods= typeBinding.getDeclaredMethods(); for (int i= 0; i < typeMethods.length; i++) { IMethodBinding curr= typeMethods[i]; int modifiers= curr.getModifiers(); if (!curr.isConstructor() && !Modifier.isStatic(modifiers) && !Modifier.isPrivate(modifiers)) { allMethods.add(curr); } } ITypeBinding superClass= typeBinding.getSuperclass(); while (superClass != null) { typeMethods= superClass.getDeclaredMethods(); for (int i= 0; i < typeMethods.length; i++) { IMethodBinding curr= typeMethods[i]; int modifiers= curr.getModifiers(); if (!curr.isConstructor() && !Modifier.isStatic(modifiers) && !Modifier.isPrivate(modifiers)) { if (findMethodBinding(curr, allMethods) == null) { allMethods.add(curr); } } } superClass= superClass.getSuperclass(); } for (int i= 0; i < allMethods.size(); i++) { IMethodBinding curr= allMethods.get(i); int modifiers= curr.getModifiers(); if ((Modifier.isAbstract(modifiers) || curr.getDeclaringClass().isInterface()) && (implementAbstractsOfInput || typeBinding != curr.getDeclaringClass())) { // implement all abstract methods toImplement.add(curr); } } HashSet<ITypeBinding> visited= new HashSet<>(); ITypeBinding curr= typeBinding; while (curr != null) { ITypeBinding[] superInterfaces= curr.getInterfaces(); for (int i= 0; i < superInterfaces.length; i++) { findUnimplementedInterfaceMethods(superInterfaces[i], visited, allMethods, typeBinding.getPackage(), toImplement); } curr= curr.getSuperclass(); } return toImplement.toArray(new IMethodBinding[toImplement.size()]); } public static IMethodBinding[] getVisibleConstructors(ITypeBinding binding, boolean accountExisting, boolean proposeDefault) { List<IMethodBinding> constructorMethods= new ArrayList<>(); List<IMethodBinding> existingConstructors= null; ITypeBinding superType= binding.getSuperclass(); if (superType == null) return new IMethodBinding[0]; if (accountExisting) { IMethodBinding[] methods= binding.getDeclaredMethods(); existingConstructors= new ArrayList<>(methods.length); for (int index= 0; index < methods.length; index++) { IMethodBinding method= methods[index]; if (method.isConstructor() && !method.isDefaultConstructor()) existingConstructors.add(method); } } if (existingConstructors != null) constructorMethods.addAll(existingConstructors); IMethodBinding[] methods= binding.getDeclaredMethods(); IMethodBinding[] superMethods= superType.getDeclaredMethods(); for (int index= 0; index < superMethods.length; index++) { IMethodBinding method= superMethods[index]; if (method.isConstructor()) { if (Bindings.isVisibleInHierarchy(method, binding.getPackage()) && (!accountExisting || !Bindings.containsSignatureEquivalentConstructor(methods, method))) constructorMethods.add(method); } } if (existingConstructors != null) constructorMethods.removeAll(existingConstructors); if (constructorMethods.isEmpty()) { superType= binding; while (superType.getSuperclass() != null) superType= superType.getSuperclass(); IMethodBinding method= Bindings.findMethodInType(superType, "Object", new ITypeBinding[0]); //$NON-NLS-1$ if (method != null) { if ((proposeDefault || !accountExisting || existingConstructors == null || existingConstructors.isEmpty()) && (!accountExisting || !Bindings.containsSignatureEquivalentConstructor(methods, method))) constructorMethods.add(method); } } return constructorMethods.toArray(new IMethodBinding[constructorMethods.size()]); }
Evaluates the insertion position of a new node.
Params:
  • listRewrite – The list rewriter to which the new node will be added
  • sibling – The Java element before which the new element should be added.
Throws:
Returns:the AST node of the list to insert before or null to insert as last.
/** * Evaluates the insertion position of a new node. * * @param listRewrite The list rewriter to which the new node will be added * @param sibling The Java element before which the new element should be added. * @return the AST node of the list to insert before or null to insert as last. * @throws JavaModelException thrown if accessing the Java element failed */
public static ASTNode getNodeToInsertBefore(ListRewrite listRewrite, IJavaElement sibling) throws JavaModelException { if (sibling instanceof IMember) { ISourceRange sourceRange= ((IMember) sibling).getSourceRange(); if (sourceRange == null) { return null; } int insertPos= sourceRange.getOffset(); List<? extends ASTNode> members= listRewrite.getOriginalList(); for (int i= 0; i < members.size(); i++) { ASTNode curr= members.get(i); if (curr.getStartPosition() >= insertPos) { return curr; } } } return null; }
Adds @Override annotation to methodDecl if not already present and if requested by code style settings or compiler errors/warnings settings.
Params:
  • settings – the code generation style settings, may be null
  • project – the Java project used to access the compiler settings
  • rewrite – the ASTRewrite
  • imports – the ImportRewrite
  • methodDecl – the method declaration to add the annotation to
  • isDeclaringTypeInterface – true if the type declaring the method overridden by methodDecl is an interface
  • group – the text edit group, may be null
/** * Adds <code>@Override</code> annotation to <code>methodDecl</code> if not already present and * if requested by code style settings or compiler errors/warnings settings. * * @param settings the code generation style settings, may be <code>null</code> * @param project the Java project used to access the compiler settings * @param rewrite the ASTRewrite * @param imports the ImportRewrite * @param methodDecl the method declaration to add the annotation to * @param isDeclaringTypeInterface <code>true</code> if the type declaring the method overridden * by <code>methodDecl</code> is an interface * @param group the text edit group, may be <code>null</code> */
public static void addOverrideAnnotation(CodeGenerationSettings settings, IJavaProject project, ASTRewrite rewrite, ImportRewrite imports, MethodDeclaration methodDecl, boolean isDeclaringTypeInterface, TextEditGroup group) { if (!JavaModelUtil.is50OrHigher(project)) { return; } if (isDeclaringTypeInterface) { String version= project.getOption(JavaCore.COMPILER_COMPLIANCE, true); if (JavaModelUtil.isVersionLessThan(version, JavaCore.VERSION_1_6)) return; // not allowed in 1.5 if (JavaCore.DISABLED.equals(project.getOption(JavaCore.COMPILER_PB_MISSING_OVERRIDE_ANNOTATION_FOR_INTERFACE_METHOD_IMPLEMENTATION, true))) return; // user doesn't want to use 1.6 style } if ((settings != null && settings.overrideAnnotation) || !JavaCore.IGNORE.equals(project.getOption(JavaCore.COMPILER_PB_MISSING_OVERRIDE_ANNOTATION, true))) { createOverrideAnnotation(rewrite, imports, methodDecl, group); } } public static void createOverrideAnnotation(ASTRewrite rewrite, ImportRewrite imports, MethodDeclaration decl, TextEditGroup group) { if (findAnnotation("java.lang.Override", decl.modifiers()) != null) { //$NON-NLS-1$ return; // No need to add duplicate annotation } AST ast= rewrite.getAST(); ASTNode root= decl.getRoot(); ImportRewriteContext context= null; if (root instanceof CompilationUnit) { context= new ContextSensitiveImportRewriteContext((CompilationUnit) root, decl.getStartPosition(), imports); } Annotation marker= ast.newMarkerAnnotation(); marker.setTypeName(ast.newName(imports.addImport("java.lang.Override", context))); //$NON-NLS-1$ rewrite.getListRewrite(decl, MethodDeclaration.MODIFIERS2_PROPERTY).insertFirst(marker, group); } public static boolean isCopyOnInheritAnnotation(ITypeBinding annotationType, IJavaProject project, EnumSet<TypeLocation> nullnessDefault, TypeLocation typeLocation) { if (JavaCore.ENABLED.equals(project.getOption(JavaCore.COMPILER_INHERIT_NULL_ANNOTATIONS, true))) return false; if (nullnessDefault != null && Bindings.isNonNullAnnotation(annotationType, project)) { if (!nullnessDefault.contains(typeLocation)) { return true; } return false; // nonnull within the scope of @NonNullByDefault: don't copy } return Bindings.isAnyNullAnnotation(annotationType, project); } public static Annotation findAnnotation(String qualifiedTypeName, List<IExtendedModifier> modifiers) { for (int i= 0; i < modifiers.size(); i++) { IExtendedModifier curr= modifiers.get(i); if (curr instanceof Annotation) { Annotation annot= (Annotation) curr; ITypeBinding binding= annot.getTypeName().resolveTypeBinding(); if (binding != null && qualifiedTypeName.equals(binding.getQualifiedName())) { return annot; } } } return null; } public static ITypeBinding replaceWildcardsAndCaptures(ITypeBinding type) { while (type.isWildcardType() || type.isCapture() || (type.isArray() && type.getElementType().isCapture())) { ITypeBinding bound = type.getBound(); type = (bound != null) ? bound : type.getErasure(); } return type; }
Creates a new stub utility.
/** * Creates a new stub utility. */
private StubUtility2Core() { // Not for instantiation } }