Copyright (c) 2000, 2018 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 Mateusz Matela - [formatter] Formatter does not format Java code correctly, especially when max line width is set
/******************************************************************************* * Copyright (c) 2000, 2018 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 * Mateusz Matela <mateusz.matela@gmail.com> - [formatter] Formatter does not format Java code correctly, especially when max line width is set *******************************************************************************/
package org.eclipse.jdt.internal.corext.codemanipulation; import org.eclipse.core.runtime.CoreException; import org.eclipse.jdt.core.Flags; import org.eclipse.jdt.core.IField; import org.eclipse.jdt.core.IJavaProject; import org.eclipse.jdt.core.IMethod; import org.eclipse.jdt.core.IType; import org.eclipse.jdt.core.JavaModelException; import org.eclipse.jdt.core.NamingConventions; import org.eclipse.jdt.core.Signature; import org.eclipse.jdt.core.dom.AST; import org.eclipse.jdt.core.dom.ASTNode; import org.eclipse.jdt.core.dom.Assignment; import org.eclipse.jdt.core.dom.Assignment.Operator; import org.eclipse.jdt.core.dom.CastExpression; import org.eclipse.jdt.core.dom.Expression; import org.eclipse.jdt.core.dom.ITypeBinding; import org.eclipse.jdt.core.dom.IVariableBinding; import org.eclipse.jdt.core.dom.InfixExpression; import org.eclipse.jdt.core.dom.NumberLiteral; import org.eclipse.jdt.core.dom.ParenthesizedExpression; import org.eclipse.jdt.core.dom.PostfixExpression; import org.eclipse.jdt.core.dom.PrefixExpression; import org.eclipse.jdt.core.dom.PrimitiveType; import org.eclipse.jdt.core.dom.rewrite.ASTRewrite; import org.eclipse.jdt.core.manipulation.CodeGeneration; import org.eclipse.jdt.internal.core.manipulation.StubUtility; import org.eclipse.jdt.internal.core.manipulation.dom.NecessaryParenthesesChecker; import org.eclipse.jdt.internal.corext.dom.ASTNodes; import org.eclipse.jdt.internal.corext.util.JavaModelUtil; import org.eclipse.jdt.internal.corext.util.JdtFlags; public class GetterSetterUtil { private static final String[] EMPTY= new String[0]; //no instances private GetterSetterUtil(){ } public static String getGetterName(IField field, String[] excludedNames) throws JavaModelException { boolean useIs= StubUtility.useIsForBooleanGetters(field.getJavaProject()); return getGetterName(field, excludedNames, useIs); } private static String getGetterName(IField field, String[] excludedNames, boolean useIsForBoolGetters) throws JavaModelException { if (excludedNames == null) { excludedNames= EMPTY; } return getGetterName(field.getJavaProject(), field.getElementName(), field.getFlags(), useIsForBoolGetters && JavaModelUtil.isBoolean(field), excludedNames); } public static String getGetterName(IVariableBinding variableType, IJavaProject project, String[] excludedNames, boolean isBoolean) { boolean useIs= StubUtility.useIsForBooleanGetters(project) && isBoolean; return getGetterName(project, variableType.getName(), variableType.getModifiers(), useIs, excludedNames); } public static String getGetterName(IJavaProject project, String fieldName, int flags, boolean isBoolean, String[] excludedNames){ return NamingConventions.suggestGetterName(project, fieldName, flags, isBoolean, excludedNames); } public static String getSetterName(IVariableBinding variableType, IJavaProject project, String[] excludedNames, boolean isBoolean) { return getSetterName(project, variableType.getName(), variableType.getModifiers(), isBoolean, excludedNames); } public static String getSetterName(IJavaProject project, String fieldName, int flags, boolean isBoolean, String[] excludedNames){ boolean useIs= StubUtility.useIsForBooleanGetters(project); return NamingConventions.suggestSetterName(project, fieldName, flags, useIs && isBoolean, excludedNames); } public static String getSetterName(IField field, String[] excludedNames) throws JavaModelException { if (excludedNames == null) { excludedNames= EMPTY; } return getSetterName(field.getJavaProject(), field.getElementName(), field.getFlags(), JavaModelUtil.isBoolean(field), excludedNames); } public static IMethod getGetter(IField field) throws JavaModelException{ String getterName= getGetterName(field, EMPTY, true); IMethod primaryCandidate= JavaModelUtil.findMethod(getterName, new String[0], false, field.getDeclaringType()); if (! JavaModelUtil.isBoolean(field) || (primaryCandidate != null && primaryCandidate.exists())) return primaryCandidate; //bug 30906 describes why we need to look for other alternatives here (try with get... for booleans) String secondCandidateName= getGetterName(field, EMPTY, false); return JavaModelUtil.findMethod(secondCandidateName, new String[0], false, field.getDeclaringType()); } public static IMethod getSetter(IField field) throws JavaModelException{ String[] args= new String[] { field.getTypeSignature() }; return JavaModelUtil.findMethod(getSetterName(field, EMPTY), args, false, field.getDeclaringType()); }
Create a stub for a getter of the given field using getter/setter templates. The resulting code has to be formatted and indented.
Params:
  • field – The field to create a getter for
  • setterName – The chosen name for the setter
  • addComments – If true, comments will be added.
  • flags – The flags signaling visibility, if static, synchronized or final
Throws:
Returns:Returns the generated stub.
/** * Create a stub for a getter of the given field using getter/setter templates. The resulting code * has to be formatted and indented. * @param field The field to create a getter for * @param setterName The chosen name for the setter * @param addComments If <code>true</code>, comments will be added. * @param flags The flags signaling visibility, if static, synchronized or final * @return Returns the generated stub. * @throws CoreException when stub creation failed */
public static String getSetterStub(IField field, String setterName, boolean addComments, int flags) throws CoreException { String fieldName= field.getElementName(); IType parentType= field.getDeclaringType(); String returnSig= field.getTypeSignature(); String typeName= Signature.toString(returnSig); IJavaProject project= field.getJavaProject(); String accessorName= StubUtility.getBaseName(field); String argname= StubUtility.suggestArgumentName(project, accessorName, EMPTY); boolean isStatic= Flags.isStatic(flags); boolean isSync= Flags.isSynchronized(flags); boolean isFinal= Flags.isFinal(flags); String lineDelim= "\n"; // Use default line delimiter, as generated stub has to be formatted anyway //$NON-NLS-1$ StringBuilder buf= new StringBuilder(); if (addComments) { String comment= CodeGeneration.getSetterComment(field.getCompilationUnit(), parentType.getTypeQualifiedName('.'), setterName, field.getElementName(), typeName, argname, accessorName, lineDelim); if (comment != null) { buf.append(comment); buf.append(lineDelim); } } buf.append(JdtFlags.getVisibilityString(flags)); buf.append(' '); if (isStatic) buf.append("static "); //$NON-NLS-1$ if (isSync) buf.append("synchronized "); //$NON-NLS-1$ if (isFinal) buf.append("final "); //$NON-NLS-1$ buf.append("void "); //$NON-NLS-1$ buf.append(setterName); buf.append('('); buf.append(typeName); buf.append(' '); buf.append(argname); buf.append(") {"); //$NON-NLS-1$ buf.append(lineDelim); boolean useThis= StubUtility.useThisForFieldAccess(project); if (argname.equals(fieldName) || (useThis && !isStatic)) { if (isStatic) fieldName= parentType.getElementName() + '.' + fieldName; else fieldName= "this." + fieldName; //$NON-NLS-1$ } String body= CodeGeneration.getSetterMethodBodyContent(field.getCompilationUnit(), parentType.getTypeQualifiedName('.'), setterName, fieldName, argname, lineDelim); if (body != null) { buf.append(body); } buf.append("}"); //$NON-NLS-1$ return buf.toString(); }
Create a stub for a getter of the given field using getter/setter templates. The resulting code has to be formatted and indented.
Params:
  • field – The field to create a getter for
  • getterName – The chosen name for the getter
  • addComments – If true, comments will be added.
  • flags – The flags signaling visibility, if static, synchronized or final
Throws:
Returns:Returns the generated stub.
/** * Create a stub for a getter of the given field using getter/setter templates. The resulting code * has to be formatted and indented. * @param field The field to create a getter for * @param getterName The chosen name for the getter * @param addComments If <code>true</code>, comments will be added. * @param flags The flags signaling visibility, if static, synchronized or final * @return Returns the generated stub. * @throws CoreException when stub creation failed */
public static String getGetterStub(IField field, String getterName, boolean addComments, int flags) throws CoreException { String fieldName= field.getElementName(); IType parentType= field.getDeclaringType(); boolean isStatic= Flags.isStatic(flags); boolean isSync= Flags.isSynchronized(flags); boolean isFinal= Flags.isFinal(flags); String typeName= Signature.toString(field.getTypeSignature()); String accessorName= StubUtility.getBaseName(field); String lineDelim= "\n"; // Use default line delimiter, as generated stub has to be formatted anyway //$NON-NLS-1$ StringBuilder buf= new StringBuilder(); if (addComments) { String comment= CodeGeneration.getGetterComment(field.getCompilationUnit(), parentType.getTypeQualifiedName('.'), getterName, field.getElementName(), typeName, accessorName, lineDelim); if (comment != null) { buf.append(comment); buf.append(lineDelim); } } buf.append(JdtFlags.getVisibilityString(flags)); buf.append(' '); if (isStatic) buf.append("static "); //$NON-NLS-1$ if (isSync) buf.append("synchronized "); //$NON-NLS-1$ if (isFinal) buf.append("final "); //$NON-NLS-1$ buf.append(typeName); buf.append(' '); buf.append(getterName); buf.append("() {"); //$NON-NLS-1$ buf.append(lineDelim); boolean useThis= StubUtility.useThisForFieldAccess(field.getJavaProject()); if (useThis && !isStatic) { fieldName= "this." + fieldName; //$NON-NLS-1$ } String body= CodeGeneration.getGetterMethodBodyContent(field.getCompilationUnit(), parentType.getTypeQualifiedName('.'), getterName, fieldName, lineDelim); if (body != null) { buf.append(body); } buf.append("}"); //$NON-NLS-1$ return buf.toString(); }
Converts an assignment, postfix expression or prefix expression into an assignable equivalent expression using the getter.
Params:
  • node – the assignment/prefix/postfix node
  • astRewrite – the astRewrite to use
  • getterExpression – the expression to insert for read accesses or null if such an expression does not exist
  • variableType – the type of the variable that the result will be assigned to
  • is50OrHigher – true if a 5.0 or higher environment can be used
Returns:an expression that can be assigned to the type variableType with node being replaced by a equivalent expression using the getter
/** * Converts an assignment, postfix expression or prefix expression into an assignable equivalent expression using the getter. * * @param node the assignment/prefix/postfix node * @param astRewrite the astRewrite to use * @param getterExpression the expression to insert for read accesses or <code>null</code> if such an expression does not exist * @param variableType the type of the variable that the result will be assigned to * @param is50OrHigher <code>true</code> if a 5.0 or higher environment can be used * @return an expression that can be assigned to the type variableType with node being replaced by a equivalent expression using the getter */
public static Expression getAssignedValue(ASTNode node, ASTRewrite astRewrite, Expression getterExpression, ITypeBinding variableType, boolean is50OrHigher) { InfixExpression.Operator op= null; AST ast= astRewrite.getAST(); if (isNotInBlock(node)) return null; if (node.getNodeType() == ASTNode.ASSIGNMENT) { Assignment assignment= ((Assignment) node); Expression rightHandSide= assignment.getRightHandSide(); Expression copiedRightOp= (Expression) astRewrite.createCopyTarget(rightHandSide); if (assignment.getOperator() == Operator.ASSIGN) { ITypeBinding rightHandSideType= rightHandSide.resolveTypeBinding(); copiedRightOp= createNarrowCastIfNessecary(copiedRightOp, rightHandSideType, ast, variableType, is50OrHigher); return copiedRightOp; } if (getterExpression != null) { InfixExpression infix= ast.newInfixExpression(); infix.setLeftOperand(getterExpression); infix.setOperator(ASTNodes.convertToInfixOperator(assignment.getOperator())); ITypeBinding infixType= infix.resolveTypeBinding(); if (NecessaryParenthesesChecker.needsParenthesesForRightOperand(rightHandSide, infix, variableType)) { ParenthesizedExpression p= ast.newParenthesizedExpression(); p.setExpression(copiedRightOp); copiedRightOp= p; } infix.setRightOperand(copiedRightOp); return createNarrowCastIfNessecary(infix, infixType, ast, variableType, is50OrHigher); } } else if (node.getNodeType() == ASTNode.POSTFIX_EXPRESSION) { PostfixExpression po= (PostfixExpression) node; if (po.getOperator() == PostfixExpression.Operator.INCREMENT) op= InfixExpression.Operator.PLUS; if (po.getOperator() == PostfixExpression.Operator.DECREMENT) op= InfixExpression.Operator.MINUS; } else if (node.getNodeType() == ASTNode.PREFIX_EXPRESSION) { PrefixExpression pe= (PrefixExpression) node; if (pe.getOperator() == PrefixExpression.Operator.INCREMENT) op= InfixExpression.Operator.PLUS; if (pe.getOperator() == PrefixExpression.Operator.DECREMENT) op= InfixExpression.Operator.MINUS; } if (op != null && getterExpression != null) { return createInfixInvocationFromPostPrefixExpression(op, getterExpression, ast, variableType, is50OrHigher); } return null; } /* * Check if the node is in a block. We don't want to update declarations */ private static boolean isNotInBlock(ASTNode parent) { ASTNode statement= parent.getParent(); boolean isStatement= statement.getNodeType() != ASTNode.EXPRESSION_STATEMENT; ASTNode block= statement.getParent(); boolean isBlock= block.getNodeType() == ASTNode.BLOCK || block.getNodeType() == ASTNode.SWITCH_STATEMENT; boolean isControlStatemenBody= ASTNodes.isControlStatementBody(statement.getLocationInParent()); return isStatement || !(isBlock || isControlStatemenBody); } private static Expression createInfixInvocationFromPostPrefixExpression(InfixExpression.Operator operator, Expression getterExpression, AST ast, ITypeBinding variableType, boolean is50OrHigher) { InfixExpression infix= ast.newInfixExpression(); infix.setLeftOperand(getterExpression); infix.setOperator(operator); NumberLiteral number= ast.newNumberLiteral(); number.setToken("1"); //$NON-NLS-1$ infix.setRightOperand(number); ITypeBinding infixType= infix.resolveTypeBinding(); return createNarrowCastIfNessecary(infix, infixType, ast, variableType, is50OrHigher); }
Checks if the assignment needs a downcast and inserts it if necessary
Params:
  • expression – the right hand-side
  • expressionType – the type of the right hand-side. Can be null
  • ast – the AST
  • variableType – the Type of the variable the expression will be assigned to
  • is50OrHigher – if true java 5.0 code will be assumed
Returns:the casted expression if necessary
/** * Checks if the assignment needs a downcast and inserts it if necessary * * @param expression the right hand-side * @param expressionType the type of the right hand-side. Can be null * @param ast the AST * @param variableType the Type of the variable the expression will be assigned to * @param is50OrHigher if <code>true</code> java 5.0 code will be assumed * @return the casted expression if necessary */
private static Expression createNarrowCastIfNessecary(Expression expression, ITypeBinding expressionType, AST ast, ITypeBinding variableType, boolean is50OrHigher) { PrimitiveType castTo= null; if (variableType.isEqualTo(expressionType)) return expression; //no cast for same type if (is50OrHigher) { if (ast.resolveWellKnownType("java.lang.Character").isEqualTo(variableType)) //$NON-NLS-1$ castTo= ast.newPrimitiveType(PrimitiveType.CHAR); if (ast.resolveWellKnownType("java.lang.Byte").isEqualTo(variableType)) //$NON-NLS-1$ castTo= ast.newPrimitiveType(PrimitiveType.BYTE); if (ast.resolveWellKnownType("java.lang.Short").isEqualTo(variableType)) //$NON-NLS-1$ castTo= ast.newPrimitiveType(PrimitiveType.SHORT); } if (ast.resolveWellKnownType("char").isEqualTo(variableType)) //$NON-NLS-1$ castTo= ast.newPrimitiveType(PrimitiveType.CHAR); if (ast.resolveWellKnownType("byte").isEqualTo(variableType)) //$NON-NLS-1$ castTo= ast.newPrimitiveType(PrimitiveType.BYTE); if (ast.resolveWellKnownType("short").isEqualTo(variableType)) //$NON-NLS-1$ castTo= ast.newPrimitiveType(PrimitiveType.SHORT); if (castTo != null) { CastExpression cast= ast.newCastExpression(); if (NecessaryParenthesesChecker.needsParentheses(expression, cast, CastExpression.EXPRESSION_PROPERTY)) { ParenthesizedExpression parenthesized= ast.newParenthesizedExpression(); parenthesized.setExpression(expression); cast.setExpression(parenthesized); } else cast.setExpression(expression); cast.setType(castTo); return cast; } return expression; } }