Copyright (c) 2000, 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 Pierre-Yves B. - Generation of equals and hashcode with java 7 Objects.equals and Objects.hashcode - https://bugs.eclipse.org/424214 Pierre-Yves B. - Different behaviour when generating hashCode and equals - https://bugs.eclipse.org/539589 Pierre-Yves B. - Confusing name when generating hashCode and equals with outer type - https://bugs.eclipse.org/539872 Red Hat Inc. - refactored to jdt.core.manipulation Pierre-Yves B. - Allow hashCode and equals generation when no fields but a super/enclosing class that implements them - https://bugs.eclipse.org/539901 Pierre-Yves B. - [hashcode/equals] Redundant null check when instanceof is used - https://bugs.eclipse.org/545424
/******************************************************************************* * Copyright (c) 2000, 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 * Pierre-Yves B. <pyvesdev@gmail.com> - Generation of equals and hashcode with java 7 Objects.equals and Objects.hashcode - https://bugs.eclipse.org/424214 * Pierre-Yves B. <pyvesdev@gmail.com> - Different behaviour when generating hashCode and equals - https://bugs.eclipse.org/539589 * Pierre-Yves B. <pyvesdev@gmail.com> - Confusing name when generating hashCode and equals with outer type - https://bugs.eclipse.org/539872 * Red Hat Inc. - refactored to jdt.core.manipulation * Pierre-Yves B. <pyvesdev@gmail.com> - Allow hashCode and equals generation when no fields but a super/enclosing class that implements them - https://bugs.eclipse.org/539901 * Pierre-Yves B. <pyvesdev@gmail.com> - [hashcode/equals] Redundant null check when instanceof is used - https://bugs.eclipse.org/545424 *******************************************************************************/
package org.eclipse.jdt.internal.corext.codemanipulation; import java.util.ArrayList; import java.util.Arrays; import java.util.Iterator; import java.util.List; import org.eclipse.core.runtime.Assert; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.NullProgressMonitor; import org.eclipse.core.runtime.OperationCanceledException; import org.eclipse.core.runtime.jobs.ISchedulingRule; import org.eclipse.core.resources.IWorkspaceRunnable; import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.text.edits.TextEdit; import org.eclipse.jdt.core.ICompilationUnit; import org.eclipse.jdt.core.IJavaElement; import org.eclipse.jdt.core.IJavaProject; import org.eclipse.jdt.core.dom.AST; import org.eclipse.jdt.core.dom.ASTNode; import org.eclipse.jdt.core.dom.AbstractTypeDeclaration; import org.eclipse.jdt.core.dom.ArrayAccess; import org.eclipse.jdt.core.dom.Assignment; import org.eclipse.jdt.core.dom.Block; import org.eclipse.jdt.core.dom.BodyDeclaration; import org.eclipse.jdt.core.dom.CastExpression; import org.eclipse.jdt.core.dom.CompilationUnit; import org.eclipse.jdt.core.dom.ConditionalExpression; import org.eclipse.jdt.core.dom.Expression; import org.eclipse.jdt.core.dom.FieldAccess; import org.eclipse.jdt.core.dom.ForStatement; import org.eclipse.jdt.core.dom.IMethodBinding; import org.eclipse.jdt.core.dom.ITypeBinding; import org.eclipse.jdt.core.dom.IVariableBinding; import org.eclipse.jdt.core.dom.IfStatement; import org.eclipse.jdt.core.dom.InfixExpression; import org.eclipse.jdt.core.dom.InfixExpression.Operator; import org.eclipse.jdt.core.dom.InstanceofExpression; 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.Modifier.ModifierKeyword; import org.eclipse.jdt.core.dom.Name; 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.ReturnStatement; import org.eclipse.jdt.core.dom.SingleVariableDeclaration; import org.eclipse.jdt.core.dom.Statement; import org.eclipse.jdt.core.dom.SuperMethodInvocation; import org.eclipse.jdt.core.dom.TagElement; import org.eclipse.jdt.core.dom.TextElement; import org.eclipse.jdt.core.dom.ThisExpression; import org.eclipse.jdt.core.dom.VariableDeclarationExpression; import org.eclipse.jdt.core.dom.VariableDeclarationFragment; import org.eclipse.jdt.core.dom.VariableDeclarationStatement; 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.dom.ASTNodeFactory; import org.eclipse.jdt.internal.corext.dom.ASTNodes; import org.eclipse.jdt.internal.corext.dom.Bindings; import org.eclipse.jdt.internal.corext.refactoring.structure.CompilationUnitRewrite; import org.eclipse.jdt.internal.corext.util.JavaModelUtil;

Workspace runnable to add implementations for Object.equals(Object) and Object.hashCode().

This implementation creates a hashCode() and an equals() method intended to be used in value types: The implementation assumes that two objects are equal (and provide the same hashCode) if all values of all fields are equal.

About the implementation:

  • To deal with reference types of fields and with supertypes, the implementation calls hashCode() and equals() on reference types of fields and on the superclass (except if the superclass is Object). It is an error if these types do not provide an equals() and hashCode() implementation and the comparison or hash code creation will fail if these methods are not correctly implemented.
  • The implementation for primitive typed fields is the same as in the JDK implementations in the wrapper types (and the java.util.Arrays class).
  • The equals() implementation uses equality of the declaring class instead of an instanceof check.
  • A test for null in equals() is only implemented in direct subclasses of Object. This is only sufficient if every subimplementation calls super.equals() before any argument checks.
  • Both equals() and hashCode() use methods from java.util.Arrays to test for equality and to generate a hash code for arrays. Note that this has an implication for Double and Float values (consider values -0.0 and 0.0 as well as border values like NaN and their equality in bit patterns) - however, the implementation is consistent with the wrapper types java.lang.Float and java.lang.Double.

Since:3.2
/** * <p> * Workspace runnable to add implementations for * <code>{@link java.lang.Object#equals(java.lang.Object)}</code> and * <code>{@link java.lang.Object#hashCode()}</code>. * </p> * * <p> * This implementation creates a hashCode() and an equals() method intended to * be used in value types: The implementation assumes that two objects are equal * (and provide the same hashCode) if all values of all fields are equal. * </p> * * <p> * About the implementation: * <ul> * <li>To deal with reference types of fields and with supertypes, the * implementation calls hashCode() and equals() on reference types of fields and * on the superclass (except if the superclass is Object). It is an error if * these types do not provide an equals() and hashCode() implementation and the * comparison or hash code creation <strong>will fail</strong> if these methods * are not correctly implemented.</li> * <li>The implementation for primitive typed fields is the same as in the JDK * implementations in the wrapper types (and the java.util.Arrays class).</li> * <li>The equals() implementation uses equality of the declaring class instead * of an instanceof check.</li> * <li>A test for null in equals() is only implemented in direct subclasses of * Object. This is only sufficient if every subimplementation calls * super.equals() before any argument checks.</li> * <li>Both equals() and hashCode() use methods from java.util.Arrays to test * for equality and to generate a hash code for arrays. Note that this has an * implication for Double and Float values (consider values -0.0 and 0.0 as well * as border values like NaN and their equality in bit patterns) - however, the * implementation is consistent with the wrapper types java.lang.Float and * java.lang.Double.</li> * </ul> * </p> * * @since 3.2 */
public final class GenerateHashCodeEqualsOperation implements IWorkspaceRunnable { private interface IHashCodeAccessProvider { public Expression getThisAccess(String name); } private static final String JAVA_UTIL_ARRAYS= "java.util.Arrays"; //$NON-NLS-1$ private static final String BOOLEAN_TRUE_CONSTANT= "1231"; //$NON-NLS-1$ private static final String BOOLEAN_FALSE_CONSTANT= "1237"; //$NON-NLS-1$ private static final String JAVA_LANG_OBJECT= "java.lang.Object"; //$NON-NLS-1$ private static final String METHODNAME_GETCLASS= "getClass"; //$NON-NLS-1$ private static final String METHODNAME_EQUALS= "equals"; //$NON-NLS-1$ private static final String METHODNAME_DEEP_EQUALS= "deepEquals"; //$NON-NLS-1$ private static final String METHODNAME_HASH_CODE= "hashCode"; //$NON-NLS-1$ private static final String METHODNAME_HASH= "hash"; //$NON-NLS-1$ private static final String METHODNAME_DEEP_HASH_CODE= "deepHashCode"; //$NON-NLS-1$ private static final String METHODNAME_GET_ENCLOSING_INSTANCE= "getEnclosingInstance"; //$NON-NLS-1$ private static final String PRIME_NUMBER= "31"; //$NON-NLS-1$ private static final String INITIAL_HASHCODE_VALUE= "1"; //$NON-NLS-1$ private static final String VARIABLE_NAME_DOUBLE_TEMPORARY= "temp"; //$NON-NLS-1$ private static final String VARIABLE_NAME_PRIME= "prime"; //$NON-NLS-1$ private static final String VARIABLE_NAME_RESULT= "result"; //$NON-NLS-1$ private static final String VARIABLE_NAME_EQUALS_PARAM= "obj"; //$NON-NLS-1$ private static final String VARIABLE_NAME_HASHCODE_PARAM= "array"; //$NON-NLS-1$ private static final String VARIABLE_NAME_EQUALS_CASTED= "other"; //$NON-NLS-1$ private static final String VARIABLE_NAME_INDEX= "index"; //$NON-NLS-1$ private static final String JAVA_UTIL_OBJECTS= "java.util.Objects"; //$NON-NLS-1$ private static final String TYPE_NAME_CLONEABLE= "Cloneable"; //$NON-NLS-1$ private static final String TYPE_NAME_SERIALIZABLE= "Serializable"; //$NON-NLS-1$ private static final String TYPE_NAME_OBJECT= "Object"; //$NON-NLS-1$
Should the resulting edit be applied?
/** Should the resulting edit be applied? */
private final boolean fApply;
The resulting text edit
/** The resulting text edit */
private TextEdit fEdit= null;
The insertion point, or null
/** The insertion point, or <code>null</code> */
private final IJavaElement fInsert;
The variable binding keys to implement
/** The variable binding keys to implement */
private final IVariableBinding[] fFields;
Should the regeneration of the methods be enforced?
/** Should the regeneration of the methods be enforced? */
private final boolean fForce;
Should the compilation unit content be saved?
/** Should the compilation unit content be saved? */
private final boolean fSave;
The code generation settings to use
/** The code generation settings to use */
private final CodeGenerationSettings fSettings;
The type declaration to add the methods to
/** The type declaration to add the methods to */
private final ITypeBinding fType;
The compilation unit ast node
/** The compilation unit ast node */
private final CompilationUnit fUnit;
The CURewrite to be used
/** The CURewrite to be used */
private final CompilationUnitRewrite fRewrite;
The ast to be used. Convenience accessor field
/** The ast to be used. Convenience accessor field */
private final AST fAst;
The number of double-typed fields handled so far
/** The number of double-typed fields handled so far */
private int fDoubleCount;
The primitive types to generate custom hashCode() methods for
/** The primitive types to generate custom hashCode() methods for */
private List<ITypeBinding> fCustomHashCodeTypes= new ArrayList<>();
true to use 'instanceof' to compare types, false otherwise
/** <code>true</code> to use 'instanceof' to compare types, <code>false</code> otherwise */
private final boolean fUseInstanceOf;
true to use newer hashcode and equals method generation, using Java 7+ Objects.hash and Objects.equals, false to generate default methods
/** * <code>true</code> to use newer hashcode and equals method generation, using Java 7+ * Objects.hash and Objects.equals, <code>false</code> to generate default methods */
private final boolean fUseJ7HashEquals;
true to use blocks for then
/** <code>true</code> to use blocks for then */
private boolean fUseBlocksForThen;
The import rewrite context, only initialized in run(IProgressMonitor).
/** The import rewrite context, only initialized in {@link #run(IProgressMonitor)}. */
private ImportRewriteContext fImportRewriteContext;
Creates a new add hash code equals operation.
Params:
  • type – the type to add the methods to
  • fields – the method binding keys to implement
  • unit – the compilation unit ast node
  • insert – the insertion point, or null
  • settings – the code generation settings to use
  • useInstanceof – true to use 'instanceof' to compare types, false otherwise
  • useJ7HashEquals – true to use Java 7+ Objects.hash and Objects.equals methods, false otherwise
  • force – true to force the regeneration of existing methods, false otherwise
  • apply – true if the resulting edit should be applied, false otherwise
  • save – true if the changed compilation unit should be saved, false otherwise
/** * Creates a new add hash code equals operation. * * @param type the type to add the methods to * @param fields the method binding keys to implement * @param unit the compilation unit ast node * @param insert the insertion point, or <code>null</code> * @param settings the code generation settings to use * @param useInstanceof <code>true</code> to use 'instanceof' to compare types, <code>false</code> otherwise * @param useJ7HashEquals <code>true</code> to use Java 7+ Objects.hash and Objects.equals methods, <code>false</code> otherwise * @param force <code>true</code> to force the regeneration of existing methods, * <code>false</code> otherwise * @param apply <code>true</code> if the resulting edit should be applied, * <code>false</code> otherwise * @param save <code>true</code> if the changed compilation unit should be * saved, <code>false</code> otherwise */
public GenerateHashCodeEqualsOperation(final ITypeBinding type, final IVariableBinding[] fields, final CompilationUnit unit, final IJavaElement insert, final CodeGenerationSettings settings, final boolean useInstanceof, final boolean useJ7HashEquals, final boolean force, final boolean apply, final boolean save) { Assert.isNotNull(type); Assert.isNotNull(fields); Assert.isNotNull(unit); Assert.isNotNull(settings); Assert.isTrue(unit.getTypeRoot() instanceof ICompilationUnit); fType= type; fInsert= insert; fUnit= unit; fFields= fields; fSettings= settings; fUseInstanceOf= useInstanceof; fUseJ7HashEquals= useJ7HashEquals; fSave= save; fApply= apply; fDoubleCount= 0; fRewrite= new CompilationUnitRewrite((ICompilationUnit) fUnit.getTypeRoot(), fUnit); fForce= force; fAst= fRewrite.getAST(); fUseBlocksForThen= false; }
Defines if then statements should use blocks or not.
Params:
  • useBlocksForThen – if set, blocks are forced in if-then statements
/** * Defines if then statements should use blocks or not. * * @param useBlocksForThen if set, blocks are forced in if-then statements */
public void setUseBlocksForThen(boolean useBlocksForThen) { fUseBlocksForThen= useBlocksForThen; }
Returns the resulting text edit.
Returns:the resulting edit
/** * Returns the resulting text edit. * * @return the resulting edit */
public final TextEdit getResultingEdit() { return fEdit; }
Returns the scheduling rule for this operation.
Returns:the scheduling rule
/** * Returns the scheduling rule for this operation. * * @return the scheduling rule */
public final ISchedulingRule getSchedulingRule() { return ResourcesPlugin.getWorkspace().getRoot(); } /* * @see org.eclipse.core.resources.IWorkspaceRunnable#run(org.eclipse.core.runtime.IProgressMonitor) */ @Override public final void run(IProgressMonitor monitor) throws CoreException { if (monitor == null) monitor= new NullProgressMonitor(); try { monitor.beginTask("", 1); //$NON-NLS-1$ monitor.setTaskName(CodeGenerationMessages.GenerateHashCodeEqualsOperation_description); fCustomHashCodeTypes.clear(); // get the declaration and the rewrite AbstractTypeDeclaration declaration= (AbstractTypeDeclaration) ASTNodes.findDeclaration(fType, fRewrite.getRoot()); ListRewrite rewriter= fRewrite.getASTRewrite().getListRewrite(declaration, declaration.getBodyDeclarationsProperty()); List<BodyDeclaration> list= declaration.bodyDeclarations(); if (fType != null && rewriter != null) { ICompilationUnit cu= (ICompilationUnit) fUnit.getJavaElement(); ASTNode insertion= StubUtility2Core.getNodeToInsertBefore(rewriter, fInsert); // equals(..) ITypeBinding[] objectAsParam= { declaration.getAST().resolveWellKnownType(JAVA_LANG_OBJECT) }; BodyDeclaration oldEquals= fForce ? findMethodToReplace(list, METHODNAME_EQUALS, objectAsParam) : null; fImportRewriteContext= new ContextSensitiveImportRewriteContext(declaration, fRewrite.getImportRewrite()); MethodDeclaration equalsMethod= createEqualsMethod(); addMethod(rewriter, insertion, equalsMethod, oldEquals); if (monitor.isCanceled()) throw new OperationCanceledException(); // hashCode() BodyDeclaration oldHash= fForce ? findMethodToReplace(list, METHODNAME_HASH_CODE, new ITypeBinding[0]) : null; MethodDeclaration hashCodeMethod= createHashCodeMethod(); addMethod(rewriter, equalsMethod, hashCodeMethod, oldHash); // helpers for (final Iterator<ITypeBinding> iterator= fCustomHashCodeTypes.iterator(); iterator.hasNext();) { final ITypeBinding binding= iterator.next(); if (findMethodToReplace(list, METHODNAME_HASH_CODE, objectAsParam) == null) { final MethodDeclaration helperDecl= createHashCodeHelper(binding); addHelper(rewriter, null, helperDecl); } } if (isMemberType()) { if (findMethodToReplace(list, METHODNAME_GET_ENCLOSING_INSTANCE, new ITypeBinding[0]) == null) { final MethodDeclaration helperDecl= createGetEnclosingInstanceHelper(); rewriter.insertLast(helperDecl, null); } } fEdit= fRewrite.createChange(true).getEdit(); if (fApply) JavaModelUtil.applyEdit(cu, fEdit, fSave, monitor); } } finally { monitor.done(); } } private boolean isMemberType() { return fType.isMember() && !Modifier.isStatic(fType.getModifiers()); } private BodyDeclaration findMethodToReplace(final List<BodyDeclaration> list, String name, ITypeBinding[] paramTypes) { for (final Iterator<BodyDeclaration> iterator= list.iterator(); iterator.hasNext();) { final BodyDeclaration bodyDecl= iterator.next(); if (bodyDecl instanceof MethodDeclaration) { final MethodDeclaration method= (MethodDeclaration) bodyDecl; final IMethodBinding binding= method.resolveBinding(); if (binding != null && binding.getName().equals(name)) { if (Bindings.equals(binding.getParameterTypes(), paramTypes)) { return method; } } } } return null; } private void addHelper(ListRewrite rewriter, ASTNode insertion, MethodDeclaration stub) { if (insertion != null) rewriter.insertBefore(stub, insertion, null); else rewriter.insertFirst(stub, null); } private void addMethod(ListRewrite rewriter, ASTNode insertion, MethodDeclaration stub, BodyDeclaration replace) { if (replace != null) { rewriter.replace(replace, stub, null); } else { if (insertion != null) rewriter.insertBefore(stub, insertion, null); else rewriter.insertLast(stub, null); } } // ******************* HASHCODE ******************* private MethodDeclaration createHashCodeMethod() throws CoreException { MethodDeclaration hashCodeMethod= fAst.newMethodDeclaration(); hashCodeMethod.modifiers().addAll(ASTNodeFactory.newModifiers(fAst, Modifier.PUBLIC)); hashCodeMethod.setName(fAst.newSimpleName(METHODNAME_HASH_CODE)); hashCodeMethod.setConstructor(false); hashCodeMethod.setReturnType2(fAst.newPrimitiveType(PrimitiveType.INT)); Block body= fAst.newBlock(); hashCodeMethod.setBody(body); boolean needsNoSuperCall= needsNoSuperCall(fType, METHODNAME_HASH_CODE, new ITypeBinding[0]); boolean memberType= isMemberType(); ReturnStatement endReturn= fAst.newReturnStatement(); if (!memberType && fFields.length == 0) { // return super.hashCode(); SuperMethodInvocation invoc= fAst.newSuperMethodInvocation(); invoc.setName(fAst.newSimpleName(METHODNAME_HASH_CODE)); endReturn.setExpression(invoc); } else if (fUseJ7HashEquals && needsNoSuperCall && !memberType && containsNoArrays()) { // return Objects.hash(...); endReturn.setExpression(createStandaloneJ7HashCall()); } else { // final int prime = 31; VariableDeclarationFragment frag= fAst.newVariableDeclarationFragment(); frag.setName(fAst.newSimpleName(VARIABLE_NAME_PRIME)); frag.setInitializer(fAst.newNumberLiteral(PRIME_NUMBER)); VariableDeclarationStatement primeNumberDeclaration= fAst.newVariableDeclarationStatement(frag); primeNumberDeclaration.modifiers().add(fAst.newModifier(ModifierKeyword.FINAL_KEYWORD)); primeNumberDeclaration.setType(fAst.newPrimitiveType(PrimitiveType.INT)); body.statements().add(primeNumberDeclaration); VariableDeclarationFragment fragment= fAst.newVariableDeclarationFragment(); fragment.setName(fAst.newSimpleName(VARIABLE_NAME_RESULT)); VariableDeclarationStatement resultDeclaration= fAst.newVariableDeclarationStatement(fragment); resultDeclaration.setType(fAst.newPrimitiveType(PrimitiveType.INT)); body.statements().add(resultDeclaration); if (needsNoSuperCall) { // int result = 1; fragment.setInitializer(fAst.newNumberLiteral(INITIAL_HASHCODE_VALUE)); } else { // int result = super.hashCode(); SuperMethodInvocation invoc= fAst.newSuperMethodInvocation(); invoc.setName(fAst.newSimpleName(METHODNAME_HASH_CODE)); fragment.setInitializer(invoc); } if (memberType) { // result = prime * result + getEnclosingInstance().hashCode(); body.statements().add(createAddEnclosingInstanceHashCode()); } MethodInvocation j7Invoc= fAst.newMethodInvocation(); for (int i= 0; i < fFields.length; i++) { if (fFields[i].getType().isArray()) body.statements().add(createAddArrayHashCode(fFields[i])); else if (fUseJ7HashEquals) j7Invoc.arguments().add(fAst.newSimpleName(fFields[i].getName())); else if (fFields[i].getType().isPrimitive()) { Statement[] sts= createAddSimpleHashCode(fFields[i].getType(), new IHashCodeAccessProvider() { @Override public Expression getThisAccess(String name) { return getThisAccessForHashCode(name); } }, fFields[i].getName(), false); for (int j= 0; j < sts.length; j++) { body.statements().add(sts[j]); } } else body.statements().add(createAddQualifiedHashCode(fFields[i])); } if (!j7Invoc.arguments().isEmpty()) { j7Invoc.setExpression(getQualifiedName(JAVA_UTIL_OBJECTS)); j7Invoc.setName(fAst.newSimpleName(METHODNAME_HASH)); body.statements().add(prepareAssignment(j7Invoc)); } endReturn.setExpression(fAst.newSimpleName(VARIABLE_NAME_RESULT)); } // the last return: body.statements().add(endReturn); // method comment if (fSettings != null) { ITypeBinding object= fAst.resolveWellKnownType(JAVA_LANG_OBJECT); IMethodBinding[] objms= object.getDeclaredMethods(); IMethodBinding objectMethod= null; for (int i= 0; i < objms.length; i++) { if (objms[i].getName().equals(METHODNAME_HASH_CODE) && objms[i].getParameterTypes().length == 0) objectMethod= objms[i]; } createMethodComment(hashCodeMethod, objectMethod); } return hashCodeMethod; } private boolean containsNoArrays() { return Arrays.stream(fFields).map(IVariableBinding::getType).noneMatch(ITypeBinding::isArray); } private MethodInvocation createStandaloneJ7HashCall() { MethodInvocation j7Invoc= fAst.newMethodInvocation(); for (int i= 0; i < fFields.length; i++) { j7Invoc.arguments().add(fAst.newSimpleName(fFields[i].getName())); } j7Invoc.setExpression(getQualifiedName(JAVA_UTIL_OBJECTS)); j7Invoc.setName(fAst.newSimpleName(METHODNAME_HASH)); return j7Invoc; } private Statement createAddEnclosingInstanceHashCode() { MethodInvocation enclosing= fAst.newMethodInvocation(); enclosing.setName(fAst.newSimpleName(METHODNAME_GET_ENCLOSING_INSTANCE)); MethodInvocation hashAccess= fAst.newMethodInvocation(); hashAccess.setName(fAst.newSimpleName(METHODNAME_HASH_CODE)); hashAccess.setExpression(enclosing); return prepareAssignment(hashAccess); } private Statement[] createAddSimpleHashCode(ITypeBinding type, IHashCodeAccessProvider provider, String name, boolean singleTemp) { List<Statement> statements= new ArrayList<>(); if (!type.isPrimitive()) { // (element == null ? 0 : element.hashCode()) ConditionalExpression ce= fAst.newConditionalExpression(); InfixExpression exp= fAst.newInfixExpression(); ArrayAccess access= fAst.newArrayAccess(); access.setArray(fAst.newSimpleName(VARIABLE_NAME_HASHCODE_PARAM)); access.setIndex(fAst.newSimpleName(VARIABLE_NAME_INDEX)); exp.setLeftOperand(access); exp.setOperator(Operator.EQUALS); exp.setRightOperand(fAst.newNullLiteral()); ce.setExpression(exp); ce.setThenExpression(fAst.newNumberLiteral("0")); //$NON-NLS-1$ MethodInvocation invoc= fAst.newMethodInvocation(); access= fAst.newArrayAccess(); access.setArray(fAst.newSimpleName(VARIABLE_NAME_HASHCODE_PARAM)); access.setIndex(fAst.newSimpleName(VARIABLE_NAME_INDEX)); invoc.setExpression(access); invoc.setName(fAst.newSimpleName(METHODNAME_HASH_CODE)); ce.setElseExpression(invoc); statements.add(prepareAssignment(parenthesize(ce))); } else if (isPrimitiveType(type, PrimitiveType.BOOLEAN)) { ConditionalExpression ce= fAst.newConditionalExpression(); ce.setExpression(provider.getThisAccess(name)); // see Boolean.hashCode(boolean) ce.setThenExpression(fAst.newNumberLiteral(BOOLEAN_TRUE_CONSTANT)); ce.setElseExpression(fAst.newNumberLiteral(BOOLEAN_FALSE_CONSTANT)); statements.add(prepareAssignment(parenthesize(ce))); } else if (isPrimitiveType(type, new PrimitiveType.Code[] { PrimitiveType.CHAR, PrimitiveType.INT, PrimitiveType.SHORT, PrimitiveType.BYTE })) { statements.add(prepareAssignment(provider.getThisAccess(name))); } else if (isPrimitiveType(type, PrimitiveType.FLOAT)) { // Float.floatToIntBits(aFloat) statements.add(prepareAssignment(createFloatInvocation(provider.getThisAccess(name)))); } else if (isPrimitiveType(type, PrimitiveType.LONG)) { statements.add(prepareAssignment(createShiftAssignment(provider.getThisAccess(name), provider.getThisAccess(name)))); } else if (isPrimitiveType(type, PrimitiveType.DOUBLE)) { VariableDeclarationFragment fragment= null; if (singleTemp || fDoubleCount == 0) { fragment= fAst.newVariableDeclarationFragment(); fragment.setName(fAst.newSimpleName(VARIABLE_NAME_DOUBLE_TEMPORARY)); VariableDeclarationStatement st2= fAst.newVariableDeclarationStatement(fragment); st2.setType(fAst.newPrimitiveType(PrimitiveType.LONG)); statements.add(st2); } fDoubleCount++; // Double.doubleToIntBits(aDouble) Expression comparison= createDoubleInvocation(provider.getThisAccess(name)); if (singleTemp) fragment.setInitializer(comparison); else { Assignment ass= fAst.newAssignment(); ass.setLeftHandSide(fAst.newSimpleName(VARIABLE_NAME_DOUBLE_TEMPORARY)); ass.setRightHandSide(comparison); statements.add(fAst.newExpressionStatement(ass)); } statements.add(prepareAssignment(createShiftAssignment(fAst.newSimpleName(VARIABLE_NAME_DOUBLE_TEMPORARY), fAst.newSimpleName(VARIABLE_NAME_DOUBLE_TEMPORARY)))); } return statements.toArray(new Statement[statements.size()]); } private Statement createAddArrayHashCode(IVariableBinding binding) { MethodInvocation invoc= fAst.newMethodInvocation(); if (JavaModelUtil.is50OrHigher(fRewrite.getCu().getJavaProject())) { if (needsDeepMethod(binding.getType())) { invoc.setName(fAst.newSimpleName(METHODNAME_DEEP_HASH_CODE)); } else { invoc.setName(fAst.newSimpleName(METHODNAME_HASH_CODE)); } invoc.setExpression(getQualifiedName(JAVA_UTIL_ARRAYS)); invoc.arguments().add(getThisAccessForHashCode(binding.getName())); } else { invoc.setName(fAst.newSimpleName(METHODNAME_HASH_CODE)); final IJavaElement element= fType.getJavaElement(); if (element != null && !"".equals(element.getElementName())) //$NON-NLS-1$ invoc.setExpression(fAst.newSimpleName(element.getElementName())); invoc.arguments().add(getThisAccessForHashCode(binding.getName())); ITypeBinding type= binding.getType().getElementType(); if (!Bindings.isVoidType(type)) { if (!type.isPrimitive() || binding.getType().getDimensions() >= 2) type= fAst.resolveWellKnownType(JAVA_LANG_OBJECT); if (!fCustomHashCodeTypes.contains(type)) fCustomHashCodeTypes.add(type); } } return prepareAssignment(invoc); } private MethodDeclaration createGetEnclosingInstanceHelper() { String enclosingTypeName= fType.getDeclaringClass().getTypeDeclaration().getName(); MethodDeclaration helperMethod= fAst.newMethodDeclaration(); helperMethod.modifiers().addAll(ASTNodeFactory.newModifiers(fAst, Modifier.PRIVATE)); helperMethod.setName(fAst.newSimpleName(METHODNAME_GET_ENCLOSING_INSTANCE)); helperMethod.setConstructor(false); helperMethod.setReturnType2(fAst.newSimpleType(fAst.newSimpleName(enclosingTypeName))); Block body= fAst.newBlock(); helperMethod.setBody(body); ThisExpression thisExpression= fAst.newThisExpression(); thisExpression.setQualifier(fAst.newSimpleName(enclosingTypeName)); ReturnStatement endReturn= fAst.newReturnStatement(); endReturn.setExpression(thisExpression); body.statements().add(endReturn); return helperMethod; } private MethodDeclaration createHashCodeHelper(ITypeBinding binding) { Assert.isTrue(!binding.isArray()); MethodDeclaration hashCodeMethod= fAst.newMethodDeclaration(); hashCodeMethod.modifiers().addAll(ASTNodeFactory.newModifiers(fAst, Modifier.PRIVATE | Modifier.STATIC)); hashCodeMethod.setName(fAst.newSimpleName(METHODNAME_HASH_CODE)); hashCodeMethod.setConstructor(false); hashCodeMethod.setReturnType2(fAst.newPrimitiveType(PrimitiveType.INT)); // ARGUMENTS List<SingleVariableDeclaration> parameters= hashCodeMethod.parameters(); SingleVariableDeclaration hashCodeParam= fAst.newSingleVariableDeclaration(); if (!binding.isPrimitive()) hashCodeParam.setType(fAst.newArrayType(fAst.newSimpleType(getQualifiedName(JAVA_LANG_OBJECT)), 1)); else hashCodeParam.setType(fAst.newArrayType(fAst.newPrimitiveType(PrimitiveType.toCode(binding.getName())), 1)); hashCodeParam.setName(fAst.newSimpleName(VARIABLE_NAME_HASHCODE_PARAM)); parameters.add(hashCodeParam); Block body= fAst.newBlock(); hashCodeMethod.setBody(body); // PRIME NUMBER VariableDeclarationFragment frag= fAst.newVariableDeclarationFragment(); frag.setName(fAst.newSimpleName(VARIABLE_NAME_PRIME)); frag.setInitializer(fAst.newNumberLiteral(PRIME_NUMBER)); VariableDeclarationStatement primeNumberDeclaration= fAst.newVariableDeclarationStatement(frag); primeNumberDeclaration.setType(fAst.newPrimitiveType(PrimitiveType.INT)); body.statements().add(primeNumberDeclaration); // IF STATEMENT IfStatement ifStatement= fAst.newIfStatement(); final InfixExpression newInfixExpression= fAst.newInfixExpression(); newInfixExpression.setLeftOperand(fAst.newSimpleName(VARIABLE_NAME_HASHCODE_PARAM)); newInfixExpression.setRightOperand(fAst.newNullLiteral()); newInfixExpression.setOperator(Operator.EQUALS); ifStatement.setExpression(newInfixExpression); final ReturnStatement returnStatement= fAst.newReturnStatement(); returnStatement.setExpression(fAst.newNumberLiteral("0")); //$NON-NLS-1$ ifStatement.setThenStatement(getThenStatement(returnStatement)); body.statements().add(ifStatement); // RESULT VariableDeclarationFragment resultFragment= fAst.newVariableDeclarationFragment(); resultFragment.setName(fAst.newSimpleName(VARIABLE_NAME_RESULT)); resultFragment.setInitializer(fAst.newNumberLiteral(INITIAL_HASHCODE_VALUE)); VariableDeclarationStatement resultDeclaration= fAst.newVariableDeclarationStatement(resultFragment); resultDeclaration.setType(fAst.newPrimitiveType(PrimitiveType.INT)); body.statements().add(resultDeclaration); // FOR LOOP ForStatement forStatement= fAst.newForStatement(); VariableDeclarationFragment indexDeclaration= fAst.newVariableDeclarationFragment(); indexDeclaration.setName(fAst.newSimpleName(VARIABLE_NAME_INDEX)); indexDeclaration.setInitializer(fAst.newNumberLiteral("0")); //$NON-NLS-1$ final VariableDeclarationExpression declExpression= fAst.newVariableDeclarationExpression(indexDeclaration); declExpression.setType(fAst.newPrimitiveType(PrimitiveType.INT)); forStatement.initializers().add(declExpression); InfixExpression infixExpr= fAst.newInfixExpression(); infixExpr.setLeftOperand(fAst.newSimpleName(VARIABLE_NAME_INDEX)); FieldAccess access= fAst.newFieldAccess(); access.setExpression(fAst.newSimpleName(VARIABLE_NAME_HASHCODE_PARAM)); access.setName(fAst.newSimpleName("length")); //$NON-NLS-1$ infixExpr.setRightOperand(access); infixExpr.setOperator(Operator.LESS); forStatement.setExpression(infixExpr); PostfixExpression postfixExpr= fAst.newPostfixExpression(); postfixExpr.setOperand(fAst.newSimpleName(VARIABLE_NAME_INDEX)); postfixExpr.setOperator(org.eclipse.jdt.core.dom.PostfixExpression.Operator.INCREMENT); forStatement.updaters().add(postfixExpr); body.statements().add(forStatement); Block forBody= fAst.newBlock(); Statement[] statements= createAddSimpleHashCode(binding, new IHashCodeAccessProvider() { @Override public Expression getThisAccess(String name) { ArrayAccess a= fAst.newArrayAccess(); a.setArray(fAst.newSimpleName(VARIABLE_NAME_HASHCODE_PARAM)); a.setIndex(fAst.newSimpleName(name)); return a; } }, VARIABLE_NAME_INDEX, true); for (int index= 0; index < statements.length; index++) { forBody.statements().add(statements[index]); } forStatement.setBody(forBody); // END RETURN ReturnStatement endReturn= fAst.newReturnStatement(); endReturn.setExpression(fAst.newSimpleName(VARIABLE_NAME_RESULT)); body.statements().add(endReturn); // COMMENT if (fSettings != null && fSettings.createComments) { Javadoc javadoc= fAst.newJavadoc(); final TagElement tagComment= fAst.newTagElement(); TextElement text= fAst.newTextElement(); text.setText(CodeGenerationMessages.GenerateHashCodeEqualsOperation_hash_code_comment); tagComment.fragments().add(text); javadoc.tags().add(tagComment); final TagElement tagParam= fAst.newTagElement(); tagParam.setTagName(CodeGenerationMessages.GenerateHashCodeEqualsOperation_tag_param); tagParam.fragments().add(fAst.newSimpleName(VARIABLE_NAME_HASHCODE_PARAM)); text= fAst.newTextElement(); text.setText(CodeGenerationMessages.GenerateHashCodeEqualsOperation_hash_code_argument); tagParam.fragments().add(text); javadoc.tags().add(tagParam); final TagElement tagReturn= fAst.newTagElement(); tagReturn.setTagName(CodeGenerationMessages.GenerateHashCodeEqualsOperation_tag_return); text= fAst.newTextElement(); text.setText(CodeGenerationMessages.GenerateHashCodeEqualsOperation_return_comment); tagReturn.fragments().add(text); javadoc.tags().add(tagReturn); hashCodeMethod.setJavadoc(javadoc); } return hashCodeMethod; } private Statement createAddQualifiedHashCode(IVariableBinding binding) { MethodInvocation invoc= fAst.newMethodInvocation(); invoc.setExpression(getThisAccessForHashCode(binding.getName())); invoc.setName(fAst.newSimpleName(METHODNAME_HASH_CODE)); InfixExpression expr= fAst.newInfixExpression(); expr.setOperator(Operator.EQUALS); expr.setLeftOperand(getThisAccessForHashCode(binding.getName())); expr.setRightOperand(fAst.newNullLiteral()); ConditionalExpression cexpr= fAst.newConditionalExpression(); cexpr.setThenExpression(fAst.newNumberLiteral("0")); //$NON-NLS-1$ cexpr.setElseExpression(invoc); cexpr.setExpression(parenthesize(expr)); return prepareAssignment(parenthesize(cexpr)); } private Expression createShiftAssignment(Expression shift1, Expression shift2) { // (int)(element ^ (element >>> 32)); // see implementation in Arrays.hashCode(), Double.hashCode() and // Long.hashCode() CastExpression ce= fAst.newCastExpression(); ce.setType(fAst.newPrimitiveType(PrimitiveType.INT)); InfixExpression unsignedShiftRight= fAst.newInfixExpression(); unsignedShiftRight.setLeftOperand(shift1); unsignedShiftRight.setRightOperand(fAst.newNumberLiteral("32")); //$NON-NLS-1$ unsignedShiftRight.setOperator(Operator.RIGHT_SHIFT_UNSIGNED); InfixExpression xor= fAst.newInfixExpression(); xor.setLeftOperand(shift2); xor.setRightOperand(parenthesize(unsignedShiftRight)); xor.setOperator(InfixExpression.Operator.XOR); ce.setExpression(parenthesize(xor)); return ce; } private Statement prepareAssignment(Expression rightHand) { // result = PRIME*result + (...) InfixExpression mul= fAst.newInfixExpression(); mul.setLeftOperand(fAst.newSimpleName(VARIABLE_NAME_PRIME)); mul.setRightOperand(fAst.newSimpleName(VARIABLE_NAME_RESULT)); mul.setOperator(Operator.TIMES); Assignment ass= fAst.newAssignment(); ass.setLeftHandSide(fAst.newSimpleName(VARIABLE_NAME_RESULT)); InfixExpression plus= fAst.newInfixExpression(); plus.setLeftOperand(mul); plus.setOperator(Operator.PLUS); plus.setRightOperand(rightHand); ass.setRightHandSide(plus); return fAst.newExpressionStatement(ass); } // *************** EQUALS *************** private MethodDeclaration createEqualsMethod() throws CoreException { MethodDeclaration equalsMethodDeclaration= fAst.newMethodDeclaration(); equalsMethodDeclaration.modifiers().addAll(ASTNodeFactory.newModifiers(fAst, Modifier.PUBLIC)); equalsMethodDeclaration.setName(fAst.newSimpleName(METHODNAME_EQUALS)); equalsMethodDeclaration.setConstructor(false); equalsMethodDeclaration.setReturnType2(fAst.newPrimitiveType(PrimitiveType.BOOLEAN)); List<SingleVariableDeclaration> parameters= equalsMethodDeclaration.parameters(); SingleVariableDeclaration equalsParam= fAst.newSingleVariableDeclaration(); equalsParam.setType(fRewrite.getImportRewrite().addImport(fAst.resolveWellKnownType(JAVA_LANG_OBJECT), fAst, fImportRewriteContext, TypeLocation.PARAMETER)); equalsParam.setName(fAst.newSimpleName(VARIABLE_NAME_EQUALS_PARAM)); parameters.add(equalsParam); Block body= fAst.newBlock(); equalsMethodDeclaration.setBody(body); // if (this == obj) return true; body.statements().add( createReturningIfStatement(fAst.newThisExpression(), fAst.newSimpleName(VARIABLE_NAME_EQUALS_PARAM), Operator.EQUALS, true)); if (needsNoSuperCall(fType, METHODNAME_EQUALS, new ITypeBinding[] {fAst.resolveWellKnownType(JAVA_LANG_OBJECT)})) { if (!fUseInstanceOf) { // if (obj == null) return false; body.statements().add( createReturningIfStatement(fAst.newSimpleName(VARIABLE_NAME_EQUALS_PARAM), fAst.newNullLiteral(), Operator.EQUALS, false)); } } else { // if (!super.equals(obj)) return false; SuperMethodInvocation superEqualsCall= fAst.newSuperMethodInvocation(); superEqualsCall.setName(fAst.newSimpleName(METHODNAME_EQUALS)); superEqualsCall.arguments().add(fAst.newSimpleName(VARIABLE_NAME_EQUALS_PARAM)); PrefixExpression pe= fAst.newPrefixExpression(); pe.setOperator(PrefixExpression.Operator.NOT); pe.setOperand(superEqualsCall); IfStatement superEqualsIf= fAst.newIfStatement(); superEqualsIf.setExpression(pe); superEqualsIf.setThenStatement(getThenStatement(getReturnFalse())); body.statements().add(superEqualsIf); } if (fUseInstanceOf) { // if (!(obj instanceof Type)) return false; InstanceofExpression expression= fAst.newInstanceofExpression(); expression.setLeftOperand(fAst.newSimpleName(VARIABLE_NAME_EQUALS_PARAM)); expression.setRightOperand(fRewrite.getImportRewrite().addImport(fType, fAst, fImportRewriteContext, TypeLocation.INSTANCEOF)); PrefixExpression notExpression= fAst.newPrefixExpression(); notExpression.setOperator(org.eclipse.jdt.core.dom.PrefixExpression.Operator.NOT); ParenthesizedExpression parenthesizedExpression= fAst.newParenthesizedExpression(); parenthesizedExpression.setExpression(expression); notExpression.setOperand(parenthesizedExpression); body.statements().add(createReturningIfStatement(false, notExpression)); } else { // if (getClass() != obj.getClass()) return false; MethodInvocation thisClass= fAst.newMethodInvocation(); thisClass.setName(fAst.newSimpleName(METHODNAME_GETCLASS)); MethodInvocation objGetClass= fAst.newMethodInvocation(); objGetClass.setExpression(fAst.newSimpleName(VARIABLE_NAME_EQUALS_PARAM)); objGetClass.setName(fAst.newSimpleName(METHODNAME_GETCLASS)); body.statements().add(createReturningIfStatement(thisClass, objGetClass, Operator.NOT_EQUALS, false)); } boolean memberType= isMemberType(); if (memberType || fFields.length > 0) { // Type other= (Type) obj; VariableDeclarationFragment sd= fAst.newVariableDeclarationFragment(); sd.setName(fAst.newSimpleName(VARIABLE_NAME_EQUALS_CASTED)); CastExpression cast= fAst.newCastExpression(); cast.setType(fAst.newSimpleType(fAst.newSimpleName(fType.getName()))); cast.setExpression(fAst.newSimpleName(VARIABLE_NAME_EQUALS_PARAM)); sd.setInitializer(cast); VariableDeclarationStatement otherDeclaration= fAst.newVariableDeclarationStatement(sd); otherDeclaration.setType(fAst.newSimpleType(fAst.newSimpleName(fType.getName()))); body.statements().add(otherDeclaration); } if (memberType) { body.statements().add(createEnclosingInstanceComparison()); } if (fUseJ7HashEquals && fFields.length > 0) { body.statements().add(createJ7EqualsStatement()); } else { for (int i= 0; i < fFields.length; i++) { IVariableBinding field= fFields[i]; ITypeBinding type= field.getType(); if (type.isPrimitive() || type.isEnum()) body.statements().add(createSimpleComparison(field)); else if (type.isArray()) { IJavaProject project= fUnit.getJavaElement().getJavaProject(); if (needsDeepMethod(type) && JavaModelUtil.is50OrHigher(project)) { body.statements().add(createMultiArrayComparison(field.getName())); } else { body.statements().add(createArrayComparison(field.getName())); } } else body.statements().add(createQualifiedComparison(field.getName())); } // the last return true: ReturnStatement endReturn= fAst.newReturnStatement(); endReturn.setExpression(fAst.newBooleanLiteral(true)); body.statements().add(endReturn); } // method comment if (fSettings != null) { ITypeBinding object= fAst.resolveWellKnownType(JAVA_LANG_OBJECT); IMethodBinding[] objms= object.getDeclaredMethods(); IMethodBinding objectMethod= null; for (int i= 0; i < objms.length; i++) { if (objms[i].getName().equals(METHODNAME_EQUALS) && objms[i].getParameterTypes().length == 1 && objms[i].getParameterTypes()[0].getQualifiedName().equals(JAVA_LANG_OBJECT)) objectMethod= objms[i]; } createMethodComment(equalsMethodDeclaration, objectMethod); } return equalsMethodDeclaration; } private Statement createJ7EqualsStatement() { // return Object.equals(anObjet, other.anObject) && ... ; ReturnStatement returnStatement= fAst.newReturnStatement(); Expression equalsExp= createJ7EqualsExpression(fFields[0]); if (fFields.length == 1) { returnStatement.setExpression(equalsExp); } else { InfixExpression exp= fAst.newInfixExpression(); exp.setLeftOperand(equalsExp); InfixExpression refExp= exp; for (int i= 1; i < fFields.length; i++) { equalsExp= createJ7EqualsExpression(fFields[i]); refExp.setOperator(Operator.CONDITIONAL_AND); if (i == fFields.length - 1) { refExp.setRightOperand(equalsExp); } else { InfixExpression infixExp= fAst.newInfixExpression(); infixExp.setLeftOperand(equalsExp); refExp.setRightOperand(infixExp); refExp= infixExp; } } returnStatement.setExpression(exp); } return returnStatement; } private Expression createJ7EqualsExpression(IVariableBinding variable) { ITypeBinding type= variable.getType(); String name= variable.getName(); if (type.isPrimitive() || type.isEnum()) { InfixExpression expression= fAst.newInfixExpression(); expression.setOperator(Operator.EQUALS); if (isPrimitiveType(type, PrimitiveType.FLOAT)) { // Float.floatToIntBits(aFloat) == Float.floatToIntBits(other.aFloat) expression.setLeftOperand(createFloatInvocation(getThisAccessForEquals(name))); expression.setRightOperand(createFloatInvocation(getOtherAccess(name))); } else if (isPrimitiveType(type, PrimitiveType.DOUBLE)) { // Double.doubleToLongBits(aDouble) == Double.doubleToLongBits(other.aDouble) expression.setLeftOperand(createDoubleInvocation(getThisAccessForEquals(name))); expression.setRightOperand(createDoubleInvocation(getOtherAccess(name))); } else { // anInt == other.anInt expression.setLeftOperand(getThisAccessForEquals(name)); expression.setRightOperand(getOtherAccess(name)); } return expression; } else { MethodInvocation invoc= fAst.newMethodInvocation(); if (type.isArray()) { // Arrays.equals(anArray, other.anArray) or Arrays.deepEquals(anArray, other.anArray) invoc.setExpression(getQualifiedName(JAVA_UTIL_ARRAYS)); invoc.setName(needsDeepMethod(type) ? fAst.newSimpleName(METHODNAME_DEEP_EQUALS) : fAst.newSimpleName(METHODNAME_EQUALS)); } else { // Objects.equals(anObj, other.anObj) invoc.setExpression(getQualifiedName(JAVA_UTIL_OBJECTS)); invoc.setName(fAst.newSimpleName(METHODNAME_EQUALS)); } invoc.arguments().add(fAst.newSimpleName(name)); invoc.arguments().add(getOtherAccess(name)); return invoc; } } private Statement createEnclosingInstanceComparison() { MethodInvocation enclosing1= fAst.newMethodInvocation(); enclosing1.setName(fAst.newSimpleName(METHODNAME_GET_ENCLOSING_INSTANCE)); MethodInvocation enclosing2= fAst.newMethodInvocation(); enclosing2.setName(fAst.newSimpleName(METHODNAME_GET_ENCLOSING_INSTANCE)); enclosing2.setExpression(fAst.newSimpleName(VARIABLE_NAME_EQUALS_CASTED)); MethodInvocation enclosingEql= fAst.newMethodInvocation(); enclosingEql.setName(fAst.newSimpleName(METHODNAME_EQUALS)); enclosingEql.setExpression(enclosing1); enclosingEql.arguments().add(enclosing2); PrefixExpression not= fAst.newPrefixExpression(); not.setOperand(enclosingEql); not.setOperator(PrefixExpression.Operator.NOT); IfStatement notEqNull= fAst.newIfStatement(); notEqNull.setExpression(not); notEqNull.setThenStatement(getThenStatement(getReturnFalse())); return notEqNull; } private Statement createSimpleComparison(IVariableBinding binding) { if (isPrimitiveType(binding.getType(), PrimitiveType.FLOAT)) { return createReturningIfStatement(createFloatInvocation(getThisAccessForEquals(binding.getName())), createFloatInvocation(getOtherAccess(binding .getName())), Operator.NOT_EQUALS, false); } else if (isPrimitiveType(binding.getType(), PrimitiveType.DOUBLE)) { return createReturningIfStatement(createDoubleInvocation(getThisAccessForEquals(binding.getName())), createDoubleInvocation(getOtherAccess(binding .getName())), Operator.NOT_EQUALS, false); } else return createReturningIfStatement(getThisAccessForEquals(binding.getName()), getOtherAccess(binding.getName()), Operator.NOT_EQUALS, false); } private Statement createArrayComparison(String name) { MethodInvocation invoc= fAst.newMethodInvocation(); invoc.setName(fAst.newSimpleName(METHODNAME_EQUALS)); invoc.setExpression(getQualifiedName(JAVA_UTIL_ARRAYS)); invoc.arguments().add(getThisAccessForEquals(name)); invoc.arguments().add(getOtherAccess(name)); PrefixExpression pe= fAst.newPrefixExpression(); pe.setOperator(PrefixExpression.Operator.NOT); pe.setOperand(invoc); IfStatement ifSt= fAst.newIfStatement(); ifSt.setExpression(pe); ifSt.setThenStatement(getThenStatement(getReturnFalse())); return ifSt; } private Statement createMultiArrayComparison(String name) { MethodInvocation invoc= fAst.newMethodInvocation(); invoc.setName(fAst.newSimpleName(METHODNAME_DEEP_EQUALS)); invoc.setExpression(getQualifiedName(JAVA_UTIL_ARRAYS)); invoc.arguments().add(getThisAccessForEquals(name)); invoc.arguments().add(getOtherAccess(name)); PrefixExpression pe= fAst.newPrefixExpression(); pe.setOperator(PrefixExpression.Operator.NOT); pe.setOperand(invoc); IfStatement ifSt= fAst.newIfStatement(); ifSt.setExpression(pe); ifSt.setThenStatement(getThenStatement(getReturnFalse())); return ifSt; }
Creates a comparison of reference types.
if (this.a == null) {
	if (other.a != null)
		return false;
} else {
	if (!this.a.equals(other.a))
		return false;
}
Params:
  • name – the field name
Returns:the comparison statement
/** * Creates a comparison of reference types. * * <pre> * if (this.a == null) { * if (other.a != null) * return false; * } else { * if (!this.a.equals(other.a)) * return false; * } * </pre> * @param name the field name * @return the comparison statement */
private Statement createQualifiedComparison(String name) { InfixExpression newCondition= fAst.newInfixExpression(); newCondition.setOperator(Operator.EQUALS); newCondition.setLeftOperand(getThisAccessForEquals(name)); newCondition.setRightOperand(fAst.newNullLiteral()); // THEN InfixExpression notEqNull= fAst.newInfixExpression(); notEqNull.setOperator(Operator.NOT_EQUALS); notEqNull.setLeftOperand(getOtherAccess(name)); notEqNull.setRightOperand(fAst.newNullLiteral()); IfStatement thenPart= fAst.newIfStatement(); thenPart.setExpression(notEqNull); thenPart.setThenStatement(getThenStatement(getReturnFalse())); Block thenPart2= fAst.newBlock(); thenPart2.statements().add(thenPart); // ELSE MethodInvocation invoc= fAst.newMethodInvocation(); invoc.setName(fAst.newSimpleName(METHODNAME_EQUALS)); invoc.setExpression(getThisAccessForEquals(name)); invoc.arguments().add(getOtherAccess(name)); PrefixExpression pe= fAst.newPrefixExpression(); pe.setOperator(PrefixExpression.Operator.NOT); pe.setOperand(invoc); IfStatement elsePart= fAst.newIfStatement(); elsePart.setExpression(pe); elsePart.setThenStatement(getThenStatement(getReturnFalse())); // ALL IfStatement isNull= fAst.newIfStatement(); isNull.setExpression(newCondition); isNull.setThenStatement(thenPart2); isNull.setElseStatement(elsePart); return isNull; } // ************************ HELPERS ************************** private Statement createReturningIfStatement(Expression left, Expression right, Operator operator, boolean whatToReturn) { InfixExpression newCondition= fAst.newInfixExpression(); newCondition.setOperator(operator); newCondition.setLeftOperand(left); newCondition.setRightOperand(right); return createReturningIfStatement(whatToReturn, newCondition); } private Statement createReturningIfStatement(boolean result, Expression condition) { IfStatement firstIf= fAst.newIfStatement(); firstIf.setExpression(condition); ReturnStatement returner= fAst.newReturnStatement(); returner.setExpression(fAst.newBooleanLiteral(result)); firstIf.setThenStatement(getThenStatement(returner)); return firstIf; } private void createMethodComment(MethodDeclaration newDeclaration, IMethodBinding copyFrom) throws CoreException { if (fSettings.createComments) { String string= CodeGeneration.getMethodComment(fRewrite.getCu(), fType.getQualifiedName(), newDeclaration, copyFrom, StubUtility.getLineDelimiterUsed(fRewrite.getCu())); if (string != null) { Javadoc javadoc= (Javadoc) fRewrite.getASTRewrite().createStringPlaceholder(string, ASTNode.JAVADOC); newDeclaration.setJavadoc(javadoc); } } IJavaProject project= fUnit.getJavaElement().getJavaProject(); StubUtility2Core.addOverrideAnnotation(fSettings, project, fRewrite.getASTRewrite(), fRewrite.getImportRewrite(), newDeclaration, copyFrom.getDeclaringClass().isInterface(), null); } private boolean needsNoSuperCall(ITypeBinding typeBinding, String name, ITypeBinding[] parameters) { Assert.isNotNull(typeBinding); IMethodBinding binding= Bindings.findMethodInHierarchy(typeBinding.getSuperclass(), name, parameters); if (binding != null && !Modifier.isAbstract(binding.getModifiers())) { ITypeBinding declaring= binding.getDeclaringClass(); return declaring.getQualifiedName().equals(JAVA_LANG_OBJECT); } return true; } private Expression getThisAccessForEquals(String name) { return getThisAccess(name, false); } private Expression getThisAccessForHashCode(String name) { return getThisAccess(name, true); } private Expression getThisAccess(String name, boolean forHashCode) { if (fSettings.useKeywordThis || needsThisQualification(name, forHashCode)) { FieldAccess fa= fAst.newFieldAccess(); fa.setExpression(fAst.newThisExpression()); fa.setName(fAst.newSimpleName(name)); return fa; } return fAst.newSimpleName(name); } private Expression getOtherAccess(String name) { return fAst.newQualifiedName(fAst.newSimpleName(VARIABLE_NAME_EQUALS_CASTED), fAst.newSimpleName(name)); } private boolean isPrimitiveType(ITypeBinding binding, PrimitiveType.Code code) { return (binding.getName().equals(code.toString())); } private boolean isPrimitiveType(ITypeBinding type, PrimitiveType.Code[] codes) { for (int i= 0; i < codes.length; i++) { PrimitiveType.Code code= codes[i]; if (isPrimitiveType(type, code)) return true; } return false; } private Name getQualifiedName(String name) { String importedType= fRewrite.getImportRewrite().addImport(name, fImportRewriteContext); return ASTNodeFactory.newName(fAst, importedType); } private ReturnStatement getReturnFalse() { ReturnStatement falseReturn= fAst.newReturnStatement(); falseReturn.setExpression(fAst.newBooleanLiteral(false)); return falseReturn; } private Statement getThenStatement(Statement statement) { if (fUseBlocksForThen && !(statement instanceof Block)) { Block block= fAst.newBlock(); block.statements().add(statement); return block; } return statement; } private Expression parenthesize(Expression expression) { ParenthesizedExpression pe= fAst.newParenthesizedExpression(); pe.setExpression(expression); return pe; } private Expression createFloatInvocation(Expression access) { return createMethodInvocation(access, "java.lang.Float", "floatToIntBits"); //$NON-NLS-1$ //$NON-NLS-2$ } private Expression createDoubleInvocation(Expression access) { return createMethodInvocation(access, "java.lang.Double", "doubleToLongBits"); //$NON-NLS-1$ //$NON-NLS-2$ } private Expression createMethodInvocation(Expression access, String qualifiedClassName, String methodName) { MethodInvocation invoc= fAst.newMethodInvocation(); invoc.setExpression(getQualifiedName(qualifiedClassName)); invoc.setName(fAst.newSimpleName(methodName)); invoc.arguments().add(access); return invoc; } private boolean needsThisQualification(String name, boolean isHashCode) { if (isHashCode) return ( (fDoubleCount > 0 && name.equals(VARIABLE_NAME_DOUBLE_TEMPORARY)) || (name.equals(VARIABLE_NAME_PRIME)) || (name .equals(VARIABLE_NAME_RESULT))); return ( (name.equals(VARIABLE_NAME_EQUALS_CASTED)) || (name.equals(VARIABLE_NAME_EQUALS_PARAM))); } private boolean needsDeepMethod(ITypeBinding type) { String elementTypeName= type.getErasure().getElementType().getName(); return type.getDimensions() > 1 || TYPE_NAME_CLONEABLE.equals(elementTypeName) || TYPE_NAME_SERIALIZABLE.equals(elementTypeName) || TYPE_NAME_OBJECT.equals(elementTypeName); } }