1 /******************************************************************************* 2 * Copyright (c) 2000, 2019 IBM Corporation and others. 3 * 4 * This program and the accompanying materials 5 * are made available under the terms of the Eclipse Public License 2.0 6 * which accompanies this distribution, and is available at 7 * https://www.eclipse.org/legal/epl-2.0/ 8 * 9 * SPDX-License-Identifier: EPL-2.0 10 * 11 * Contributors: 12 * IBM Corporation - initial API and implementation 13 * Pierre-Yves B. <pyvesdev@gmail.com> - Generation of equals and hashcode with java 7 Objects.equals and Objects.hashcode - https://bugs.eclipse.org/424214 14 * Pierre-Yves B. <pyvesdev@gmail.com> - Different behaviour when generating hashCode and equals - https://bugs.eclipse.org/539589 15 * Pierre-Yves B. <pyvesdev@gmail.com> - Confusing name when generating hashCode and equals with outer type - https://bugs.eclipse.org/539872 16 * Red Hat Inc. - refactored to jdt.core.manipulation 17 * 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 18 * Pierre-Yves B. <pyvesdev@gmail.com> - [hashcode/equals] Redundant null check when instanceof is used - https://bugs.eclipse.org/545424 19 *******************************************************************************/ 20 package org.eclipse.jdt.internal.corext.codemanipulation; 21 22 import java.util.ArrayList; 23 import java.util.Arrays; 24 import java.util.List; 25 26 import org.eclipse.core.runtime.Assert; 27 import org.eclipse.core.runtime.CoreException; 28 import org.eclipse.core.runtime.IProgressMonitor; 29 import org.eclipse.core.runtime.NullProgressMonitor; 30 import org.eclipse.core.runtime.OperationCanceledException; 31 import org.eclipse.core.runtime.jobs.ISchedulingRule; 32 33 import org.eclipse.core.resources.IWorkspaceRunnable; 34 import org.eclipse.core.resources.ResourcesPlugin; 35 36 import org.eclipse.text.edits.TextEdit; 37 38 import org.eclipse.jdt.core.ICompilationUnit; 39 import org.eclipse.jdt.core.IJavaElement; 40 import org.eclipse.jdt.core.IJavaProject; 41 import org.eclipse.jdt.core.dom.AST; 42 import org.eclipse.jdt.core.dom.ASTNode; 43 import org.eclipse.jdt.core.dom.AbstractTypeDeclaration; 44 import org.eclipse.jdt.core.dom.ArrayAccess; 45 import org.eclipse.jdt.core.dom.Assignment; 46 import org.eclipse.jdt.core.dom.Block; 47 import org.eclipse.jdt.core.dom.BodyDeclaration; 48 import org.eclipse.jdt.core.dom.CastExpression; 49 import org.eclipse.jdt.core.dom.CompilationUnit; 50 import org.eclipse.jdt.core.dom.ConditionalExpression; 51 import org.eclipse.jdt.core.dom.Expression; 52 import org.eclipse.jdt.core.dom.FieldAccess; 53 import org.eclipse.jdt.core.dom.ForStatement; 54 import org.eclipse.jdt.core.dom.IMethodBinding; 55 import org.eclipse.jdt.core.dom.ITypeBinding; 56 import org.eclipse.jdt.core.dom.IVariableBinding; 57 import org.eclipse.jdt.core.dom.IfStatement; 58 import org.eclipse.jdt.core.dom.InfixExpression; 59 import org.eclipse.jdt.core.dom.InfixExpression.Operator; 60 import org.eclipse.jdt.core.dom.InstanceofExpression; 61 import org.eclipse.jdt.core.dom.Javadoc; 62 import org.eclipse.jdt.core.dom.MethodDeclaration; 63 import org.eclipse.jdt.core.dom.MethodInvocation; 64 import org.eclipse.jdt.core.dom.Modifier; 65 import org.eclipse.jdt.core.dom.Modifier.ModifierKeyword; 66 import org.eclipse.jdt.core.dom.Name; 67 import org.eclipse.jdt.core.dom.ParenthesizedExpression; 68 import org.eclipse.jdt.core.dom.PostfixExpression; 69 import org.eclipse.jdt.core.dom.PrefixExpression; 70 import org.eclipse.jdt.core.dom.PrimitiveType; 71 import org.eclipse.jdt.core.dom.PrimitiveType.Code; 72 import org.eclipse.jdt.core.dom.ReturnStatement; 73 import org.eclipse.jdt.core.dom.SingleVariableDeclaration; 74 import org.eclipse.jdt.core.dom.Statement; 75 import org.eclipse.jdt.core.dom.SuperMethodInvocation; 76 import org.eclipse.jdt.core.dom.TagElement; 77 import org.eclipse.jdt.core.dom.TextElement; 78 import org.eclipse.jdt.core.dom.ThisExpression; 79 import org.eclipse.jdt.core.dom.VariableDeclarationExpression; 80 import org.eclipse.jdt.core.dom.VariableDeclarationFragment; 81 import org.eclipse.jdt.core.dom.VariableDeclarationStatement; 82 import org.eclipse.jdt.core.dom.rewrite.ImportRewrite.ImportRewriteContext; 83 import org.eclipse.jdt.core.dom.rewrite.ImportRewrite.TypeLocation; 84 import org.eclipse.jdt.core.dom.rewrite.ListRewrite; 85 import org.eclipse.jdt.core.manipulation.CodeGeneration; 86 87 import org.eclipse.jdt.internal.core.manipulation.StubUtility; 88 import org.eclipse.jdt.internal.corext.dom.ASTNodeFactory; 89 import org.eclipse.jdt.internal.corext.dom.ASTNodes; 90 import org.eclipse.jdt.internal.corext.dom.Bindings; 91 import org.eclipse.jdt.internal.corext.refactoring.structure.CompilationUnitRewrite; 92 import org.eclipse.jdt.internal.corext.util.JavaModelUtil; 93 94 /** 95 * <p> 96 * Workspace runnable to add implementations for 97 * <code>{@link java.lang.Object#equals(java.lang.Object)}</code> and 98 * <code>{@link java.lang.Object#hashCode()}</code>. 99 * </p> 100 * 101 * <p> 102 * This implementation creates a hashCode() and an equals() method intended to 103 * be used in value types: The implementation assumes that two objects are equal 104 * (and provide the same hashCode) if all values of all fields are equal. 105 * </p> 106 * 107 * <p> 108 * About the implementation: 109 * <ul> 110 * <li>To deal with reference types of fields and with supertypes, the 111 * implementation calls hashCode() and equals() on reference types of fields and 112 * on the superclass (except if the superclass is Object). It is an error if 113 * these types do not provide an equals() and hashCode() implementation and the 114 * comparison or hash code creation <strong>will fail</strong> if these methods 115 * are not correctly implemented.</li> 116 * <li>The implementation for primitive typed fields is the same as in the JDK 117 * implementations in the wrapper types (and the java.util.Arrays class).</li> 118 * <li>The equals() implementation uses equality of the declaring class instead 119 * of an instanceof check.</li> 120 * <li>A test for null in equals() is only implemented in direct subclasses of 121 * Object. This is only sufficient if every subimplementation calls 122 * super.equals() before any argument checks.</li> 123 * <li>Both equals() and hashCode() use methods from java.util.Arrays to test 124 * for equality and to generate a hash code for arrays. Note that this has an 125 * implication for Double and Float values (consider values -0.0 and 0.0 as well 126 * as border values like NaN and their equality in bit patterns) - however, the 127 * implementation is consistent with the wrapper types java.lang.Float and 128 * java.lang.Double.</li> 129 * </ul> 130 * </p> 131 * 132 * @since 3.2 133 */ 134 public final class GenerateHashCodeEqualsOperation implements IWorkspaceRunnable { 135 136 private interface IHashCodeAccessProvider { 137 getThisAccess(String name)138 public Expression getThisAccess(String name); 139 } 140 141 private static final String JAVA_UTIL_ARRAYS= "java.util.Arrays"; //$NON-NLS-1$ 142 143 private static final String BOOLEAN_TRUE_CONSTANT= "1231"; //$NON-NLS-1$ 144 145 private static final String BOOLEAN_FALSE_CONSTANT= "1237"; //$NON-NLS-1$ 146 147 private static final String JAVA_LANG_OBJECT= "java.lang.Object"; //$NON-NLS-1$ 148 149 private static final String METHODNAME_GETCLASS= "getClass"; //$NON-NLS-1$ 150 151 private static final String METHODNAME_EQUALS= "equals"; //$NON-NLS-1$ 152 153 private static final String METHODNAME_DEEP_EQUALS= "deepEquals"; //$NON-NLS-1$ 154 155 private static final String METHODNAME_HASH_CODE= "hashCode"; //$NON-NLS-1$ 156 157 private static final String METHODNAME_HASH= "hash"; //$NON-NLS-1$ 158 159 private static final String METHODNAME_DEEP_HASH_CODE= "deepHashCode"; //$NON-NLS-1$ 160 161 private static final String METHODNAME_GET_ENCLOSING_INSTANCE= "getEnclosingInstance"; //$NON-NLS-1$ 162 163 private static final String PRIME_NUMBER= "31"; //$NON-NLS-1$ 164 165 private static final String INITIAL_HASHCODE_VALUE= "1"; //$NON-NLS-1$ 166 167 private static final String VARIABLE_NAME_DOUBLE_TEMPORARY= "temp"; //$NON-NLS-1$ 168 169 private static final String VARIABLE_NAME_PRIME= "prime"; //$NON-NLS-1$ 170 171 private static final String VARIABLE_NAME_RESULT= "result"; //$NON-NLS-1$ 172 173 private static final String VARIABLE_NAME_EQUALS_PARAM= "obj"; //$NON-NLS-1$ 174 175 private static final String VARIABLE_NAME_HASHCODE_PARAM= "array"; //$NON-NLS-1$ 176 177 private static final String VARIABLE_NAME_EQUALS_CASTED= "other"; //$NON-NLS-1$ 178 179 private static final String VARIABLE_NAME_INDEX= "index"; //$NON-NLS-1$ 180 181 private static final String JAVA_UTIL_OBJECTS= "java.util.Objects"; //$NON-NLS-1$ 182 183 private static final String TYPE_NAME_CLONEABLE= "Cloneable"; //$NON-NLS-1$ 184 185 private static final String TYPE_NAME_SERIALIZABLE= "Serializable"; //$NON-NLS-1$ 186 187 private static final String TYPE_NAME_OBJECT= "Object"; //$NON-NLS-1$ 188 189 /** Should the resulting edit be applied? */ 190 private final boolean fApply; 191 192 /** The resulting text edit */ 193 private TextEdit fEdit= null; 194 195 /** The insertion point, or <code>null</code> */ 196 private final IJavaElement fInsert; 197 198 /** The variable binding keys to implement */ 199 private final IVariableBinding[] fFields; 200 201 /** Should the regeneration of the methods be enforced? */ 202 private final boolean fForce; 203 204 /** Should the compilation unit content be saved? */ 205 private final boolean fSave; 206 207 /** The code generation settings to use */ 208 private final CodeGenerationSettings fSettings; 209 210 /** The type declaration to add the methods to */ 211 private final ITypeBinding fType; 212 213 /** The compilation unit ast node */ 214 private final CompilationUnit fUnit; 215 216 /** The CURewrite to be used */ 217 private final CompilationUnitRewrite fRewrite; 218 219 /** The ast to be used. Convenience accessor field */ 220 private final AST fAst; 221 222 /** The number of double-typed fields handled so far */ 223 private int fDoubleCount; 224 225 /** The primitive types to generate custom hashCode() methods for */ 226 private List<ITypeBinding> fCustomHashCodeTypes= new ArrayList<>(); 227 228 /** <code>true</code> to use 'instanceof' to compare types, <code>false</code> otherwise */ 229 private final boolean fUseInstanceOf; 230 231 /** 232 * <code>true</code> to use newer hashcode and equals method generation, using Java 7+ 233 * Objects.hash and Objects.equals, <code>false</code> to generate default methods 234 */ 235 private final boolean fUseJ7HashEquals; 236 237 /** <code>true</code> to use blocks for then */ 238 private boolean fUseBlocksForThen; 239 240 /** The import rewrite context, only initialized in {@link #run(IProgressMonitor)}. */ 241 private ImportRewriteContext fImportRewriteContext; 242 243 /** 244 * Creates a new add hash code equals operation. 245 * 246 * @param type the type to add the methods to 247 * @param fields the method binding keys to implement 248 * @param unit the compilation unit ast node 249 * @param insert the insertion point, or <code>null</code> 250 * @param settings the code generation settings to use 251 * @param useInstanceof <code>true</code> to use 'instanceof' to compare types, <code>false</code> otherwise 252 * @param useJ7HashEquals <code>true</code> to use Java 7+ Objects.hash and Objects.equals methods, <code>false</code> otherwise 253 * @param force <code>true</code> to force the regeneration of existing methods, 254 * <code>false</code> otherwise 255 * @param apply <code>true</code> if the resulting edit should be applied, 256 * <code>false</code> otherwise 257 * @param save <code>true</code> if the changed compilation unit should be 258 * saved, <code>false</code> otherwise 259 */ 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)260 public GenerateHashCodeEqualsOperation(final ITypeBinding type, final IVariableBinding[] fields, final CompilationUnit unit, 261 final IJavaElement insert, final CodeGenerationSettings settings, final boolean useInstanceof, final boolean useJ7HashEquals, final boolean force, final boolean apply, 262 final boolean save) { 263 Assert.isNotNull(type); 264 Assert.isNotNull(fields); 265 Assert.isNotNull(unit); 266 Assert.isNotNull(settings); 267 Assert.isTrue(unit.getTypeRoot() instanceof ICompilationUnit); 268 269 fType= type; 270 fInsert= insert; 271 fUnit= unit; 272 fFields= fields; 273 fSettings= settings; 274 fUseInstanceOf= useInstanceof; 275 fUseJ7HashEquals= useJ7HashEquals; 276 fSave= save; 277 fApply= apply; 278 fDoubleCount= 0; 279 fRewrite= new CompilationUnitRewrite((ICompilationUnit) fUnit.getTypeRoot(), fUnit); 280 fForce= force; 281 fAst= fRewrite.getAST(); 282 fUseBlocksForThen= false; 283 } 284 285 /** 286 * Defines if then statements should use blocks or not. 287 * 288 * @param useBlocksForThen if set, blocks are forced in if-then statements 289 */ setUseBlocksForThen(boolean useBlocksForThen)290 public void setUseBlocksForThen(boolean useBlocksForThen) { 291 fUseBlocksForThen= useBlocksForThen; 292 } 293 294 /** 295 * Returns the resulting text edit. 296 * 297 * @return the resulting edit 298 */ getResultingEdit()299 public final TextEdit getResultingEdit() { 300 return fEdit; 301 } 302 303 /** 304 * Returns the scheduling rule for this operation. 305 * 306 * @return the scheduling rule 307 */ getSchedulingRule()308 public final ISchedulingRule getSchedulingRule() { 309 return ResourcesPlugin.getWorkspace().getRoot(); 310 } 311 312 /* 313 * @see org.eclipse.core.resources.IWorkspaceRunnable#run(org.eclipse.core.runtime.IProgressMonitor) 314 */ 315 @Override run(IProgressMonitor monitor)316 public final void run(IProgressMonitor monitor) throws CoreException { 317 if (monitor == null) 318 monitor= new NullProgressMonitor(); 319 try { 320 monitor.beginTask("", 1); //$NON-NLS-1$ 321 monitor.setTaskName(CodeGenerationMessages.GenerateHashCodeEqualsOperation_description); 322 323 fCustomHashCodeTypes.clear(); 324 325 // get the declaration and the rewrite 326 AbstractTypeDeclaration declaration= (AbstractTypeDeclaration) ASTNodes.findDeclaration(fType, fRewrite.getRoot()); 327 ListRewrite rewriter= fRewrite.getASTRewrite().getListRewrite(declaration, declaration.getBodyDeclarationsProperty()); 328 List<BodyDeclaration> list= declaration.bodyDeclarations(); 329 if (fType != null && rewriter != null) { 330 331 ICompilationUnit cu= (ICompilationUnit) fUnit.getJavaElement(); 332 333 ASTNode insertion= StubUtility2Core.getNodeToInsertBefore(rewriter, fInsert); 334 335 // equals(..) 336 ITypeBinding[] objectAsParam= { declaration.getAST().resolveWellKnownType(JAVA_LANG_OBJECT) }; 337 BodyDeclaration oldEquals= fForce ? findMethodToReplace(list, METHODNAME_EQUALS, objectAsParam) : null; 338 339 fImportRewriteContext= new ContextSensitiveImportRewriteContext(declaration, fRewrite.getImportRewrite()); 340 MethodDeclaration equalsMethod= createEqualsMethod(); 341 addMethod(rewriter, insertion, equalsMethod, oldEquals); 342 343 if (monitor.isCanceled()) 344 throw new OperationCanceledException(); 345 346 // hashCode() 347 BodyDeclaration oldHash= fForce ? findMethodToReplace(list, METHODNAME_HASH_CODE, new ITypeBinding[0]) : null; 348 349 MethodDeclaration hashCodeMethod= createHashCodeMethod(); 350 addMethod(rewriter, equalsMethod, hashCodeMethod, oldHash); 351 352 // helpers 353 for (ITypeBinding binding : fCustomHashCodeTypes) { 354 if (findMethodToReplace(list, METHODNAME_HASH_CODE, objectAsParam) == null) { 355 final MethodDeclaration helperDecl= createHashCodeHelper(binding); 356 addHelper(rewriter, null, helperDecl); 357 } 358 } 359 360 if (isMemberType()) { 361 if (findMethodToReplace(list, METHODNAME_GET_ENCLOSING_INSTANCE, new ITypeBinding[0]) == null) { 362 final MethodDeclaration helperDecl= createGetEnclosingInstanceHelper(); 363 rewriter.insertLast(helperDecl, null); 364 } 365 } 366 367 fEdit= fRewrite.createChange(true).getEdit(); 368 if (fApply) 369 JavaModelUtil.applyEdit(cu, fEdit, fSave, monitor); 370 } 371 } finally { 372 monitor.done(); 373 } 374 } 375 isMemberType()376 private boolean isMemberType() { 377 return fType.isMember() && !Modifier.isStatic(fType.getModifiers()); 378 } 379 findMethodToReplace(final List<BodyDeclaration> list, String name, ITypeBinding[] paramTypes)380 private BodyDeclaration findMethodToReplace(final List<BodyDeclaration> list, String name, ITypeBinding[] paramTypes) { 381 for (BodyDeclaration bodyDecl : list) { 382 if (bodyDecl instanceof MethodDeclaration) { 383 final MethodDeclaration method= (MethodDeclaration) bodyDecl; 384 final IMethodBinding binding= method.resolveBinding(); 385 if (binding != null && binding.getName().equals(name)) { 386 if (Bindings.equals(binding.getParameterTypes(), paramTypes)) { 387 return method; 388 } 389 } 390 } 391 } 392 return null; 393 } 394 addHelper(ListRewrite rewriter, ASTNode insertion, MethodDeclaration stub)395 private void addHelper(ListRewrite rewriter, ASTNode insertion, MethodDeclaration stub) { 396 if (insertion != null) 397 rewriter.insertBefore(stub, insertion, null); 398 else 399 rewriter.insertFirst(stub, null); 400 } 401 addMethod(ListRewrite rewriter, ASTNode insertion, MethodDeclaration stub, BodyDeclaration replace)402 private void addMethod(ListRewrite rewriter, ASTNode insertion, MethodDeclaration stub, BodyDeclaration replace) { 403 if (replace != null) { 404 rewriter.replace(replace, stub, null); 405 } else { 406 if (insertion != null) 407 rewriter.insertBefore(stub, insertion, null); 408 else 409 rewriter.insertLast(stub, null); 410 } 411 } 412 413 // ******************* HASHCODE ******************* 414 createHashCodeMethod()415 private MethodDeclaration createHashCodeMethod() throws CoreException { 416 417 MethodDeclaration hashCodeMethod= fAst.newMethodDeclaration(); 418 hashCodeMethod.modifiers().addAll(ASTNodeFactory.newModifiers(fAst, Modifier.PUBLIC)); 419 hashCodeMethod.setName(fAst.newSimpleName(METHODNAME_HASH_CODE)); 420 hashCodeMethod.setConstructor(false); 421 hashCodeMethod.setReturnType2(fAst.newPrimitiveType(PrimitiveType.INT)); 422 423 Block body= fAst.newBlock(); 424 hashCodeMethod.setBody(body); 425 426 boolean needsNoSuperCall= needsNoSuperCall(fType, METHODNAME_HASH_CODE, new ITypeBinding[0]); 427 boolean memberType= isMemberType(); 428 ReturnStatement endReturn= fAst.newReturnStatement(); 429 if (!memberType && fFields.length == 0) { 430 // return super.hashCode(); 431 SuperMethodInvocation invoc= fAst.newSuperMethodInvocation(); 432 invoc.setName(fAst.newSimpleName(METHODNAME_HASH_CODE)); 433 endReturn.setExpression(invoc); 434 } else if (fUseJ7HashEquals && needsNoSuperCall && !memberType && containsNoArrays()) { 435 // return Objects.hash(...); 436 endReturn.setExpression(createStandaloneJ7HashCall()); 437 } else { 438 // final int prime = 31; 439 VariableDeclarationFragment frag= fAst.newVariableDeclarationFragment(); 440 frag.setName(fAst.newSimpleName(VARIABLE_NAME_PRIME)); 441 frag.setInitializer(fAst.newNumberLiteral(PRIME_NUMBER)); 442 443 VariableDeclarationStatement primeNumberDeclaration= fAst.newVariableDeclarationStatement(frag); 444 primeNumberDeclaration.modifiers().add(fAst.newModifier(ModifierKeyword.FINAL_KEYWORD)); 445 primeNumberDeclaration.setType(fAst.newPrimitiveType(PrimitiveType.INT)); 446 body.statements().add(primeNumberDeclaration); 447 448 VariableDeclarationFragment fragment= fAst.newVariableDeclarationFragment(); 449 fragment.setName(fAst.newSimpleName(VARIABLE_NAME_RESULT)); 450 451 VariableDeclarationStatement resultDeclaration= fAst.newVariableDeclarationStatement(fragment); 452 resultDeclaration.setType(fAst.newPrimitiveType(PrimitiveType.INT)); 453 body.statements().add(resultDeclaration); 454 455 if (needsNoSuperCall) { 456 // int result = 1; 457 fragment.setInitializer(fAst.newNumberLiteral(INITIAL_HASHCODE_VALUE)); 458 } else { 459 // int result = super.hashCode(); 460 SuperMethodInvocation invoc= fAst.newSuperMethodInvocation(); 461 invoc.setName(fAst.newSimpleName(METHODNAME_HASH_CODE)); 462 fragment.setInitializer(invoc); 463 } 464 465 if (memberType) { 466 // result = prime * result + getEnclosingInstance().hashCode(); 467 body.statements().add(createAddEnclosingInstanceHashCode()); 468 } 469 470 MethodInvocation j7Invoc= fAst.newMethodInvocation(); 471 for (IVariableBinding field : fFields) { 472 if (field.getType().isArray()) { 473 body.statements().add(createAddArrayHashCode(field)); 474 } else if (fUseJ7HashEquals) { 475 j7Invoc.arguments().add(fAst.newSimpleName(field.getName())); 476 } else if (field.getType().isPrimitive()) { 477 Statement[] sts= createAddSimpleHashCode(field.getType(), this::getThisAccessForHashCode, field.getName(), false); 478 for (Statement st : sts) { 479 body.statements().add(st); 480 } 481 } else { 482 body.statements().add(createAddQualifiedHashCode(field)); 483 } 484 } 485 if (!j7Invoc.arguments().isEmpty()) { 486 j7Invoc.setExpression(getQualifiedName(JAVA_UTIL_OBJECTS)); 487 j7Invoc.setName(fAst.newSimpleName(METHODNAME_HASH)); 488 body.statements().add(prepareAssignment(j7Invoc)); 489 } 490 endReturn.setExpression(fAst.newSimpleName(VARIABLE_NAME_RESULT)); 491 } 492 493 // the last return: 494 body.statements().add(endReturn); 495 496 // method comment 497 if (fSettings != null) { 498 ITypeBinding object= fAst.resolveWellKnownType(JAVA_LANG_OBJECT); 499 IMethodBinding objectMethod= null; 500 for (IMethodBinding objm : object.getDeclaredMethods()) { 501 if (objm.getName().equals(METHODNAME_HASH_CODE) && objm.getParameterTypes().length == 0) { 502 objectMethod= objm; 503 } 504 } 505 createMethodComment(hashCodeMethod, objectMethod); 506 } 507 508 return hashCodeMethod; 509 } 510 containsNoArrays()511 private boolean containsNoArrays() { 512 return Arrays.stream(fFields).map(IVariableBinding::getType).noneMatch(ITypeBinding::isArray); 513 } 514 createStandaloneJ7HashCall()515 private MethodInvocation createStandaloneJ7HashCall() { 516 MethodInvocation j7Invoc= fAst.newMethodInvocation(); 517 for (IVariableBinding field : fFields) { 518 j7Invoc.arguments().add(fAst.newSimpleName(field.getName())); 519 } 520 j7Invoc.setExpression(getQualifiedName(JAVA_UTIL_OBJECTS)); 521 j7Invoc.setName(fAst.newSimpleName(METHODNAME_HASH)); 522 return j7Invoc; 523 } 524 createAddEnclosingInstanceHashCode()525 private Statement createAddEnclosingInstanceHashCode() { 526 MethodInvocation enclosing= fAst.newMethodInvocation(); 527 enclosing.setName(fAst.newSimpleName(METHODNAME_GET_ENCLOSING_INSTANCE)); 528 MethodInvocation hashAccess= fAst.newMethodInvocation(); 529 hashAccess.setName(fAst.newSimpleName(METHODNAME_HASH_CODE)); 530 hashAccess.setExpression(enclosing); 531 return prepareAssignment(hashAccess); 532 } 533 createAddSimpleHashCode(ITypeBinding type, IHashCodeAccessProvider provider, String name, boolean singleTemp)534 private Statement[] createAddSimpleHashCode(ITypeBinding type, IHashCodeAccessProvider provider, String name, boolean singleTemp) { 535 536 List<Statement> statements= new ArrayList<>(); 537 538 if (!type.isPrimitive()) { 539 // (element == null ? 0 : element.hashCode()) 540 ConditionalExpression ce= fAst.newConditionalExpression(); 541 InfixExpression exp= fAst.newInfixExpression(); 542 ArrayAccess access= fAst.newArrayAccess(); 543 access.setArray(fAst.newSimpleName(VARIABLE_NAME_HASHCODE_PARAM)); 544 access.setIndex(fAst.newSimpleName(VARIABLE_NAME_INDEX)); 545 exp.setLeftOperand(access); 546 exp.setOperator(Operator.EQUALS); 547 exp.setRightOperand(fAst.newNullLiteral()); 548 ce.setExpression(exp); 549 ce.setThenExpression(fAst.newNumberLiteral("0")); //$NON-NLS-1$ 550 MethodInvocation invoc= fAst.newMethodInvocation(); 551 access= fAst.newArrayAccess(); 552 access.setArray(fAst.newSimpleName(VARIABLE_NAME_HASHCODE_PARAM)); 553 access.setIndex(fAst.newSimpleName(VARIABLE_NAME_INDEX)); 554 invoc.setExpression(access); 555 invoc.setName(fAst.newSimpleName(METHODNAME_HASH_CODE)); 556 ce.setElseExpression(invoc); 557 statements.add(prepareAssignment(parenthesize(ce))); 558 } else if (isPrimitiveType(type, PrimitiveType.BOOLEAN)) { 559 ConditionalExpression ce= fAst.newConditionalExpression(); 560 ce.setExpression(provider.getThisAccess(name)); 561 // see Boolean.hashCode(boolean) 562 ce.setThenExpression(fAst.newNumberLiteral(BOOLEAN_TRUE_CONSTANT)); 563 ce.setElseExpression(fAst.newNumberLiteral(BOOLEAN_FALSE_CONSTANT)); 564 statements.add(prepareAssignment(parenthesize(ce))); 565 } else if (isPrimitiveType(type, new PrimitiveType.Code[] { PrimitiveType.CHAR, PrimitiveType.INT, PrimitiveType.SHORT, PrimitiveType.BYTE })) { 566 statements.add(prepareAssignment(provider.getThisAccess(name))); 567 } else if (isPrimitiveType(type, PrimitiveType.FLOAT)) { 568 // Float.floatToIntBits(aFloat) 569 statements.add(prepareAssignment(createFloatInvocation(provider.getThisAccess(name)))); 570 } else if (isPrimitiveType(type, PrimitiveType.LONG)) { 571 statements.add(prepareAssignment(createShiftAssignment(provider.getThisAccess(name), provider.getThisAccess(name)))); 572 } else if (isPrimitiveType(type, PrimitiveType.DOUBLE)) { 573 574 VariableDeclarationFragment fragment= null; 575 if (singleTemp || fDoubleCount == 0) { 576 fragment= fAst.newVariableDeclarationFragment(); 577 fragment.setName(fAst.newSimpleName(VARIABLE_NAME_DOUBLE_TEMPORARY)); 578 579 VariableDeclarationStatement st2= fAst.newVariableDeclarationStatement(fragment); 580 st2.setType(fAst.newPrimitiveType(PrimitiveType.LONG)); 581 statements.add(st2); 582 } 583 fDoubleCount++; 584 585 // Double.doubleToIntBits(aDouble) 586 Expression comparison= createDoubleInvocation(provider.getThisAccess(name)); 587 588 if (singleTemp) 589 fragment.setInitializer(comparison); 590 else { 591 Assignment ass= fAst.newAssignment(); 592 ass.setLeftHandSide(fAst.newSimpleName(VARIABLE_NAME_DOUBLE_TEMPORARY)); 593 ass.setRightHandSide(comparison); 594 statements.add(fAst.newExpressionStatement(ass)); 595 } 596 statements.add(prepareAssignment(createShiftAssignment(fAst.newSimpleName(VARIABLE_NAME_DOUBLE_TEMPORARY), fAst.newSimpleName(VARIABLE_NAME_DOUBLE_TEMPORARY)))); 597 } 598 599 return statements.toArray(new Statement[statements.size()]); 600 } 601 createAddArrayHashCode(IVariableBinding binding)602 private Statement createAddArrayHashCode(IVariableBinding binding) { 603 MethodInvocation invoc= fAst.newMethodInvocation(); 604 if (JavaModelUtil.is50OrHigher(fRewrite.getCu().getJavaProject())) { 605 if (needsDeepMethod(binding.getType())) { 606 invoc.setName(fAst.newSimpleName(METHODNAME_DEEP_HASH_CODE)); 607 } else { 608 invoc.setName(fAst.newSimpleName(METHODNAME_HASH_CODE)); 609 } 610 invoc.setExpression(getQualifiedName(JAVA_UTIL_ARRAYS)); 611 invoc.arguments().add(getThisAccessForHashCode(binding.getName())); 612 } else { 613 invoc.setName(fAst.newSimpleName(METHODNAME_HASH_CODE)); 614 final IJavaElement element= fType.getJavaElement(); 615 if (element != null && !"".equals(element.getElementName())) //$NON-NLS-1$ 616 invoc.setExpression(fAst.newSimpleName(element.getElementName())); 617 invoc.arguments().add(getThisAccessForHashCode(binding.getName())); 618 ITypeBinding type= binding.getType().getElementType(); 619 if (!Bindings.isVoidType(type)) { 620 if (!type.isPrimitive() || binding.getType().getDimensions() >= 2) 621 type= fAst.resolveWellKnownType(JAVA_LANG_OBJECT); 622 if (!fCustomHashCodeTypes.contains(type)) 623 fCustomHashCodeTypes.add(type); 624 } 625 } 626 return prepareAssignment(invoc); 627 } 628 createGetEnclosingInstanceHelper()629 private MethodDeclaration createGetEnclosingInstanceHelper() { 630 String enclosingTypeName= fType.getDeclaringClass().getTypeDeclaration().getName(); 631 632 MethodDeclaration helperMethod= fAst.newMethodDeclaration(); 633 helperMethod.modifiers().addAll(ASTNodeFactory.newModifiers(fAst, Modifier.PRIVATE)); 634 helperMethod.setName(fAst.newSimpleName(METHODNAME_GET_ENCLOSING_INSTANCE)); 635 helperMethod.setConstructor(false); 636 helperMethod.setReturnType2(fAst.newSimpleType(fAst.newSimpleName(enclosingTypeName))); 637 638 Block body= fAst.newBlock(); 639 helperMethod.setBody(body); 640 641 ThisExpression thisExpression= fAst.newThisExpression(); 642 thisExpression.setQualifier(fAst.newSimpleName(enclosingTypeName)); 643 644 ReturnStatement endReturn= fAst.newReturnStatement(); 645 endReturn.setExpression(thisExpression); 646 body.statements().add(endReturn); 647 648 return helperMethod; 649 } 650 651 createHashCodeHelper(ITypeBinding binding)652 private MethodDeclaration createHashCodeHelper(ITypeBinding binding) { 653 Assert.isTrue(!binding.isArray()); 654 655 MethodDeclaration hashCodeMethod= fAst.newMethodDeclaration(); 656 hashCodeMethod.modifiers().addAll(ASTNodeFactory.newModifiers(fAst, Modifier.PRIVATE | Modifier.STATIC)); 657 hashCodeMethod.setName(fAst.newSimpleName(METHODNAME_HASH_CODE)); 658 hashCodeMethod.setConstructor(false); 659 hashCodeMethod.setReturnType2(fAst.newPrimitiveType(PrimitiveType.INT)); 660 661 // ARGUMENTS 662 List<SingleVariableDeclaration> parameters= hashCodeMethod.parameters(); 663 SingleVariableDeclaration hashCodeParam= fAst.newSingleVariableDeclaration(); 664 if (!binding.isPrimitive()) 665 hashCodeParam.setType(fAst.newArrayType(fAst.newSimpleType(getQualifiedName(JAVA_LANG_OBJECT)), 1)); 666 else 667 hashCodeParam.setType(fAst.newArrayType(fAst.newPrimitiveType(PrimitiveType.toCode(binding.getName())), 1)); 668 hashCodeParam.setName(fAst.newSimpleName(VARIABLE_NAME_HASHCODE_PARAM)); 669 parameters.add(hashCodeParam); 670 671 Block body= fAst.newBlock(); 672 hashCodeMethod.setBody(body); 673 674 // PRIME NUMBER 675 VariableDeclarationFragment frag= fAst.newVariableDeclarationFragment(); 676 frag.setName(fAst.newSimpleName(VARIABLE_NAME_PRIME)); 677 frag.setInitializer(fAst.newNumberLiteral(PRIME_NUMBER)); 678 679 VariableDeclarationStatement primeNumberDeclaration= fAst.newVariableDeclarationStatement(frag); 680 primeNumberDeclaration.setType(fAst.newPrimitiveType(PrimitiveType.INT)); 681 body.statements().add(primeNumberDeclaration); 682 683 // IF STATEMENT 684 IfStatement ifStatement= fAst.newIfStatement(); 685 final InfixExpression newInfixExpression= fAst.newInfixExpression(); 686 newInfixExpression.setLeftOperand(fAst.newSimpleName(VARIABLE_NAME_HASHCODE_PARAM)); 687 newInfixExpression.setRightOperand(fAst.newNullLiteral()); 688 newInfixExpression.setOperator(Operator.EQUALS); 689 ifStatement.setExpression(newInfixExpression); 690 final ReturnStatement returnStatement= fAst.newReturnStatement(); 691 returnStatement.setExpression(fAst.newNumberLiteral("0")); //$NON-NLS-1$ 692 ifStatement.setThenStatement(getThenStatement(returnStatement)); 693 body.statements().add(ifStatement); 694 695 // RESULT 696 VariableDeclarationFragment resultFragment= fAst.newVariableDeclarationFragment(); 697 resultFragment.setName(fAst.newSimpleName(VARIABLE_NAME_RESULT)); 698 resultFragment.setInitializer(fAst.newNumberLiteral(INITIAL_HASHCODE_VALUE)); 699 VariableDeclarationStatement resultDeclaration= fAst.newVariableDeclarationStatement(resultFragment); 700 resultDeclaration.setType(fAst.newPrimitiveType(PrimitiveType.INT)); 701 body.statements().add(resultDeclaration); 702 703 // FOR LOOP 704 ForStatement forStatement= fAst.newForStatement(); 705 706 VariableDeclarationFragment indexDeclaration= fAst.newVariableDeclarationFragment(); 707 indexDeclaration.setName(fAst.newSimpleName(VARIABLE_NAME_INDEX)); 708 indexDeclaration.setInitializer(fAst.newNumberLiteral("0")); //$NON-NLS-1$ 709 final VariableDeclarationExpression declExpression= fAst.newVariableDeclarationExpression(indexDeclaration); 710 declExpression.setType(fAst.newPrimitiveType(PrimitiveType.INT)); 711 forStatement.initializers().add(declExpression); 712 InfixExpression infixExpr= fAst.newInfixExpression(); 713 infixExpr.setLeftOperand(fAst.newSimpleName(VARIABLE_NAME_INDEX)); 714 FieldAccess access= fAst.newFieldAccess(); 715 access.setExpression(fAst.newSimpleName(VARIABLE_NAME_HASHCODE_PARAM)); 716 access.setName(fAst.newSimpleName("length")); //$NON-NLS-1$ 717 infixExpr.setRightOperand(access); 718 infixExpr.setOperator(Operator.LESS); 719 forStatement.setExpression(infixExpr); 720 PostfixExpression postfixExpr= fAst.newPostfixExpression(); 721 postfixExpr.setOperand(fAst.newSimpleName(VARIABLE_NAME_INDEX)); 722 postfixExpr.setOperator(org.eclipse.jdt.core.dom.PostfixExpression.Operator.INCREMENT); 723 forStatement.updaters().add(postfixExpr); 724 body.statements().add(forStatement); 725 726 Block forBody= fAst.newBlock(); 727 Statement[] statements= createAddSimpleHashCode(binding, name -> { 728 ArrayAccess a= fAst.newArrayAccess(); 729 a.setArray(fAst.newSimpleName(VARIABLE_NAME_HASHCODE_PARAM)); 730 a.setIndex(fAst.newSimpleName(name)); 731 return a; 732 }, VARIABLE_NAME_INDEX, true); 733 for (Statement statement : statements) { 734 forBody.statements().add(statement); 735 } 736 forStatement.setBody(forBody); 737 738 // END RETURN 739 ReturnStatement endReturn= fAst.newReturnStatement(); 740 endReturn.setExpression(fAst.newSimpleName(VARIABLE_NAME_RESULT)); 741 body.statements().add(endReturn); 742 743 // COMMENT 744 if (fSettings != null && fSettings.createComments) { 745 Javadoc javadoc= fAst.newJavadoc(); 746 final TagElement tagComment= fAst.newTagElement(); 747 TextElement text= fAst.newTextElement(); 748 text.setText(CodeGenerationMessages.GenerateHashCodeEqualsOperation_hash_code_comment); 749 tagComment.fragments().add(text); 750 javadoc.tags().add(tagComment); 751 final TagElement tagParam= fAst.newTagElement(); 752 tagParam.setTagName(CodeGenerationMessages.GenerateHashCodeEqualsOperation_tag_param); 753 tagParam.fragments().add(fAst.newSimpleName(VARIABLE_NAME_HASHCODE_PARAM)); 754 text= fAst.newTextElement(); 755 text.setText(CodeGenerationMessages.GenerateHashCodeEqualsOperation_hash_code_argument); 756 tagParam.fragments().add(text); 757 javadoc.tags().add(tagParam); 758 final TagElement tagReturn= fAst.newTagElement(); 759 tagReturn.setTagName(CodeGenerationMessages.GenerateHashCodeEqualsOperation_tag_return); 760 text= fAst.newTextElement(); 761 text.setText(CodeGenerationMessages.GenerateHashCodeEqualsOperation_return_comment); 762 tagReturn.fragments().add(text); 763 javadoc.tags().add(tagReturn); 764 hashCodeMethod.setJavadoc(javadoc); 765 } 766 return hashCodeMethod; 767 } 768 createAddQualifiedHashCode(IVariableBinding binding)769 private Statement createAddQualifiedHashCode(IVariableBinding binding) { 770 771 MethodInvocation invoc= fAst.newMethodInvocation(); 772 invoc.setExpression(getThisAccessForHashCode(binding.getName())); 773 invoc.setName(fAst.newSimpleName(METHODNAME_HASH_CODE)); 774 775 InfixExpression expr= fAst.newInfixExpression(); 776 expr.setOperator(Operator.EQUALS); 777 expr.setLeftOperand(getThisAccessForHashCode(binding.getName())); 778 expr.setRightOperand(fAst.newNullLiteral()); 779 780 ConditionalExpression cexpr= fAst.newConditionalExpression(); 781 cexpr.setThenExpression(fAst.newNumberLiteral("0")); //$NON-NLS-1$ 782 cexpr.setElseExpression(invoc); 783 cexpr.setExpression(parenthesize(expr)); 784 785 return prepareAssignment(parenthesize(cexpr)); 786 } 787 createShiftAssignment(Expression shift1, Expression shift2)788 private Expression createShiftAssignment(Expression shift1, Expression shift2) { 789 // (int)(element ^ (element >>> 32)); 790 // see implementation in Arrays.hashCode(), Double.hashCode() and 791 // Long.hashCode() 792 CastExpression ce= fAst.newCastExpression(); 793 ce.setType(fAst.newPrimitiveType(PrimitiveType.INT)); 794 795 InfixExpression unsignedShiftRight= fAst.newInfixExpression(); 796 unsignedShiftRight.setLeftOperand(shift1); 797 unsignedShiftRight.setRightOperand(fAst.newNumberLiteral("32")); //$NON-NLS-1$ 798 unsignedShiftRight.setOperator(Operator.RIGHT_SHIFT_UNSIGNED); 799 800 InfixExpression xor= fAst.newInfixExpression(); 801 xor.setLeftOperand(shift2); 802 xor.setRightOperand(parenthesize(unsignedShiftRight)); 803 xor.setOperator(InfixExpression.Operator.XOR); 804 805 ce.setExpression(parenthesize(xor)); 806 return ce; 807 } 808 prepareAssignment(Expression rightHand)809 private Statement prepareAssignment(Expression rightHand) { 810 // result = PRIME*result + (...) 811 InfixExpression mul= fAst.newInfixExpression(); 812 mul.setLeftOperand(fAst.newSimpleName(VARIABLE_NAME_PRIME)); 813 mul.setRightOperand(fAst.newSimpleName(VARIABLE_NAME_RESULT)); 814 mul.setOperator(Operator.TIMES); 815 816 Assignment ass= fAst.newAssignment(); 817 ass.setLeftHandSide(fAst.newSimpleName(VARIABLE_NAME_RESULT)); 818 819 InfixExpression plus= fAst.newInfixExpression(); 820 plus.setLeftOperand(mul); 821 plus.setOperator(Operator.PLUS); 822 plus.setRightOperand(rightHand); 823 824 ass.setRightHandSide(plus); 825 826 return fAst.newExpressionStatement(ass); 827 } 828 829 // *************** EQUALS *************** 830 createEqualsMethod()831 private MethodDeclaration createEqualsMethod() throws CoreException { 832 833 MethodDeclaration equalsMethodDeclaration= fAst.newMethodDeclaration(); 834 equalsMethodDeclaration.modifiers().addAll(ASTNodeFactory.newModifiers(fAst, Modifier.PUBLIC)); 835 equalsMethodDeclaration.setName(fAst.newSimpleName(METHODNAME_EQUALS)); 836 equalsMethodDeclaration.setConstructor(false); 837 equalsMethodDeclaration.setReturnType2(fAst.newPrimitiveType(PrimitiveType.BOOLEAN)); 838 839 List<SingleVariableDeclaration> parameters= equalsMethodDeclaration.parameters(); 840 SingleVariableDeclaration equalsParam= fAst.newSingleVariableDeclaration(); 841 equalsParam.setType(fRewrite.getImportRewrite().addImport(fAst.resolveWellKnownType(JAVA_LANG_OBJECT), fAst, fImportRewriteContext, TypeLocation.PARAMETER)); 842 equalsParam.setName(fAst.newSimpleName(VARIABLE_NAME_EQUALS_PARAM)); 843 parameters.add(equalsParam); 844 845 Block body= fAst.newBlock(); 846 equalsMethodDeclaration.setBody(body); 847 848 // if (this == obj) return true; 849 body.statements().add( 850 createReturningIfStatement(fAst.newThisExpression(), fAst.newSimpleName(VARIABLE_NAME_EQUALS_PARAM), Operator.EQUALS, true)); 851 852 if (needsNoSuperCall(fType, METHODNAME_EQUALS, new ITypeBinding[] {fAst.resolveWellKnownType(JAVA_LANG_OBJECT)})) { 853 if (!fUseInstanceOf) { 854 // if (obj == null) return false; 855 body.statements().add( 856 createReturningIfStatement(fAst.newSimpleName(VARIABLE_NAME_EQUALS_PARAM), fAst.newNullLiteral(), Operator.EQUALS, false)); 857 } 858 } else { 859 // if (!super.equals(obj)) return false; 860 SuperMethodInvocation superEqualsCall= fAst.newSuperMethodInvocation(); 861 superEqualsCall.setName(fAst.newSimpleName(METHODNAME_EQUALS)); 862 superEqualsCall.arguments().add(fAst.newSimpleName(VARIABLE_NAME_EQUALS_PARAM)); 863 864 PrefixExpression pe= fAst.newPrefixExpression(); 865 pe.setOperator(PrefixExpression.Operator.NOT); 866 pe.setOperand(superEqualsCall); 867 868 IfStatement superEqualsIf= fAst.newIfStatement(); 869 superEqualsIf.setExpression(pe); 870 superEqualsIf.setThenStatement(getThenStatement(getReturnFalse())); 871 872 body.statements().add(superEqualsIf); 873 } 874 875 if (fUseInstanceOf) { 876 // if (!(obj instanceof Type)) return false; 877 InstanceofExpression expression= fAst.newInstanceofExpression(); 878 expression.setLeftOperand(fAst.newSimpleName(VARIABLE_NAME_EQUALS_PARAM)); 879 expression.setRightOperand(fRewrite.getImportRewrite().addImport(fType, fAst, fImportRewriteContext, TypeLocation.INSTANCEOF)); 880 881 PrefixExpression notExpression= fAst.newPrefixExpression(); 882 notExpression.setOperator(org.eclipse.jdt.core.dom.PrefixExpression.Operator.NOT); 883 884 ParenthesizedExpression parenthesizedExpression= fAst.newParenthesizedExpression(); 885 parenthesizedExpression.setExpression(expression); 886 887 notExpression.setOperand(parenthesizedExpression); 888 889 body.statements().add(createReturningIfStatement(false, notExpression)); 890 } else { 891 // if (getClass() != obj.getClass()) return false; 892 MethodInvocation thisClass= fAst.newMethodInvocation(); 893 thisClass.setName(fAst.newSimpleName(METHODNAME_GETCLASS)); 894 895 MethodInvocation objGetClass= fAst.newMethodInvocation(); 896 objGetClass.setExpression(fAst.newSimpleName(VARIABLE_NAME_EQUALS_PARAM)); 897 objGetClass.setName(fAst.newSimpleName(METHODNAME_GETCLASS)); 898 899 body.statements().add(createReturningIfStatement(thisClass, objGetClass, Operator.NOT_EQUALS, false)); 900 } 901 902 boolean memberType= isMemberType(); 903 if (memberType || fFields.length > 0) { 904 // Type other= (Type) obj; 905 VariableDeclarationFragment sd= fAst.newVariableDeclarationFragment(); 906 sd.setName(fAst.newSimpleName(VARIABLE_NAME_EQUALS_CASTED)); 907 908 CastExpression cast= fAst.newCastExpression(); 909 cast.setType(fAst.newSimpleType(fAst.newSimpleName(fType.getName()))); 910 cast.setExpression(fAst.newSimpleName(VARIABLE_NAME_EQUALS_PARAM)); 911 sd.setInitializer(cast); 912 913 VariableDeclarationStatement otherDeclaration= fAst.newVariableDeclarationStatement(sd); 914 otherDeclaration.setType(fAst.newSimpleType(fAst.newSimpleName(fType.getName()))); 915 916 body.statements().add(otherDeclaration); 917 } 918 919 if (memberType) { 920 body.statements().add(createEnclosingInstanceComparison()); 921 } 922 923 if (fUseJ7HashEquals && fFields.length > 0) { 924 body.statements().add(createJ7EqualsStatement()); 925 } else { 926 for (IVariableBinding field : fFields) { 927 ITypeBinding type= field.getType(); 928 if (type.isPrimitive() || type.isEnum()) 929 body.statements().add(createSimpleComparison(field)); 930 else if (type.isArray()) { 931 IJavaProject project= fUnit.getJavaElement().getJavaProject(); 932 if (needsDeepMethod(type) && JavaModelUtil.is50OrHigher(project)) { 933 body.statements().add(createMultiArrayComparison(field.getName())); 934 } else { 935 body.statements().add(createArrayComparison(field.getName())); 936 } 937 } else 938 body.statements().add(createQualifiedComparison(field.getName())); 939 } 940 941 // the last return true: 942 ReturnStatement endReturn= fAst.newReturnStatement(); 943 endReturn.setExpression(fAst.newBooleanLiteral(true)); 944 945 body.statements().add(endReturn); 946 } 947 948 // method comment 949 if (fSettings != null) { 950 ITypeBinding object= fAst.resolveWellKnownType(JAVA_LANG_OBJECT); 951 IMethodBinding objectMethod= null; 952 for (IMethodBinding objm : object.getDeclaredMethods()) { 953 if (objm.getName().equals(METHODNAME_EQUALS) && objm.getParameterTypes().length == 1 && objm.getParameterTypes()[0].getQualifiedName().equals(JAVA_LANG_OBJECT)) { 954 objectMethod= objm; 955 } 956 } 957 createMethodComment(equalsMethodDeclaration, objectMethod); 958 } 959 960 return equalsMethodDeclaration; 961 } 962 createJ7EqualsStatement()963 private Statement createJ7EqualsStatement() { 964 // return Object.equals(anObjet, other.anObject) && ... ; 965 ReturnStatement returnStatement= fAst.newReturnStatement(); 966 967 Expression equalsExp= createJ7EqualsExpression(fFields[0]); 968 if (fFields.length == 1) { 969 returnStatement.setExpression(equalsExp); 970 } else { 971 InfixExpression exp= fAst.newInfixExpression(); 972 exp.setLeftOperand(equalsExp); 973 InfixExpression refExp= exp; 974 for (int i= 1; i < fFields.length; i++) { 975 equalsExp= createJ7EqualsExpression(fFields[i]); 976 977 refExp.setOperator(Operator.CONDITIONAL_AND); 978 if (i == fFields.length - 1) { 979 refExp.setRightOperand(equalsExp); 980 } else { 981 InfixExpression infixExp= fAst.newInfixExpression(); 982 infixExp.setLeftOperand(equalsExp); 983 refExp.setRightOperand(infixExp); 984 refExp= infixExp; 985 } 986 } 987 returnStatement.setExpression(exp); 988 } 989 return returnStatement; 990 } 991 createJ7EqualsExpression(IVariableBinding variable)992 private Expression createJ7EqualsExpression(IVariableBinding variable) { 993 ITypeBinding type= variable.getType(); 994 String name= variable.getName(); 995 if (type.isPrimitive() || type.isEnum()) { 996 InfixExpression expression= fAst.newInfixExpression(); 997 expression.setOperator(Operator.EQUALS); 998 if (isPrimitiveType(type, PrimitiveType.FLOAT)) { 999 // Float.floatToIntBits(aFloat) == Float.floatToIntBits(other.aFloat) 1000 expression.setLeftOperand(createFloatInvocation(getThisAccessForEquals(name))); 1001 expression.setRightOperand(createFloatInvocation(getOtherAccess(name))); 1002 } else if (isPrimitiveType(type, PrimitiveType.DOUBLE)) { 1003 // Double.doubleToLongBits(aDouble) == Double.doubleToLongBits(other.aDouble) 1004 expression.setLeftOperand(createDoubleInvocation(getThisAccessForEquals(name))); 1005 expression.setRightOperand(createDoubleInvocation(getOtherAccess(name))); 1006 } else { 1007 // anInt == other.anInt 1008 expression.setLeftOperand(getThisAccessForEquals(name)); 1009 expression.setRightOperand(getOtherAccess(name)); 1010 } 1011 return expression; 1012 } else { 1013 MethodInvocation invoc= fAst.newMethodInvocation(); 1014 if (type.isArray()) { 1015 // Arrays.equals(anArray, other.anArray) or Arrays.deepEquals(anArray, other.anArray) 1016 invoc.setExpression(getQualifiedName(JAVA_UTIL_ARRAYS)); 1017 invoc.setName(needsDeepMethod(type) ? fAst.newSimpleName(METHODNAME_DEEP_EQUALS) : fAst.newSimpleName(METHODNAME_EQUALS)); 1018 } else { 1019 // Objects.equals(anObj, other.anObj) 1020 invoc.setExpression(getQualifiedName(JAVA_UTIL_OBJECTS)); 1021 invoc.setName(fAst.newSimpleName(METHODNAME_EQUALS)); 1022 } 1023 invoc.arguments().add(getThisAccessForEquals(name)); 1024 invoc.arguments().add(getOtherAccess(name)); 1025 return invoc; 1026 } 1027 } 1028 createEnclosingInstanceComparison()1029 private Statement createEnclosingInstanceComparison() { 1030 MethodInvocation enclosing1= fAst.newMethodInvocation(); 1031 enclosing1.setName(fAst.newSimpleName(METHODNAME_GET_ENCLOSING_INSTANCE)); 1032 1033 MethodInvocation enclosing2= fAst.newMethodInvocation(); 1034 enclosing2.setName(fAst.newSimpleName(METHODNAME_GET_ENCLOSING_INSTANCE)); 1035 enclosing2.setExpression(fAst.newSimpleName(VARIABLE_NAME_EQUALS_CASTED)); 1036 1037 MethodInvocation enclosingEql= fAst.newMethodInvocation(); 1038 enclosingEql.setName(fAst.newSimpleName(METHODNAME_EQUALS)); 1039 enclosingEql.setExpression(enclosing1); 1040 enclosingEql.arguments().add(enclosing2); 1041 1042 PrefixExpression not= fAst.newPrefixExpression(); 1043 not.setOperand(enclosingEql); 1044 not.setOperator(PrefixExpression.Operator.NOT); 1045 1046 IfStatement notEqNull= fAst.newIfStatement(); 1047 notEqNull.setExpression(not); 1048 notEqNull.setThenStatement(getThenStatement(getReturnFalse())); 1049 return notEqNull; 1050 } 1051 createSimpleComparison(IVariableBinding binding)1052 private Statement createSimpleComparison(IVariableBinding binding) { 1053 if (isPrimitiveType(binding.getType(), PrimitiveType.FLOAT)) { 1054 return createReturningIfStatement(createFloatInvocation(getThisAccessForEquals(binding.getName())), createFloatInvocation(getOtherAccess(binding 1055 .getName())), Operator.NOT_EQUALS, false); 1056 } else if (isPrimitiveType(binding.getType(), PrimitiveType.DOUBLE)) { 1057 return createReturningIfStatement(createDoubleInvocation(getThisAccessForEquals(binding.getName())), createDoubleInvocation(getOtherAccess(binding 1058 .getName())), Operator.NOT_EQUALS, false); 1059 } else 1060 return createReturningIfStatement(getThisAccessForEquals(binding.getName()), getOtherAccess(binding.getName()), Operator.NOT_EQUALS, false); 1061 } 1062 createArrayComparison(String name)1063 private Statement createArrayComparison(String name) { 1064 MethodInvocation invoc= fAst.newMethodInvocation(); 1065 invoc.setName(fAst.newSimpleName(METHODNAME_EQUALS)); 1066 invoc.setExpression(getQualifiedName(JAVA_UTIL_ARRAYS)); 1067 invoc.arguments().add(getThisAccessForEquals(name)); 1068 invoc.arguments().add(getOtherAccess(name)); 1069 1070 PrefixExpression pe= fAst.newPrefixExpression(); 1071 pe.setOperator(PrefixExpression.Operator.NOT); 1072 pe.setOperand(invoc); 1073 1074 IfStatement ifSt= fAst.newIfStatement(); 1075 ifSt.setExpression(pe); 1076 ifSt.setThenStatement(getThenStatement(getReturnFalse())); 1077 1078 return ifSt; 1079 } 1080 createMultiArrayComparison(String name)1081 private Statement createMultiArrayComparison(String name) { 1082 MethodInvocation invoc= fAst.newMethodInvocation(); 1083 invoc.setName(fAst.newSimpleName(METHODNAME_DEEP_EQUALS)); 1084 invoc.setExpression(getQualifiedName(JAVA_UTIL_ARRAYS)); 1085 invoc.arguments().add(getThisAccessForEquals(name)); 1086 invoc.arguments().add(getOtherAccess(name)); 1087 1088 PrefixExpression pe= fAst.newPrefixExpression(); 1089 pe.setOperator(PrefixExpression.Operator.NOT); 1090 pe.setOperand(invoc); 1091 1092 IfStatement ifSt= fAst.newIfStatement(); 1093 ifSt.setExpression(pe); 1094 ifSt.setThenStatement(getThenStatement(getReturnFalse())); 1095 1096 return ifSt; 1097 } 1098 1099 /** 1100 * Creates a comparison of reference types. 1101 * 1102 * <pre> 1103 * if (this.a == null) { 1104 * if (other.a != null) 1105 * return false; 1106 * } else { 1107 * if (!this.a.equals(other.a)) 1108 * return false; 1109 * } 1110 * </pre> 1111 * @param name the field name 1112 * @return the comparison statement 1113 */ createQualifiedComparison(String name)1114 private Statement createQualifiedComparison(String name) { 1115 InfixExpression newCondition= fAst.newInfixExpression(); 1116 newCondition.setOperator(Operator.EQUALS); 1117 newCondition.setLeftOperand(getThisAccessForEquals(name)); 1118 newCondition.setRightOperand(fAst.newNullLiteral()); 1119 1120 // THEN 1121 InfixExpression notEqNull= fAst.newInfixExpression(); 1122 notEqNull.setOperator(Operator.NOT_EQUALS); 1123 notEqNull.setLeftOperand(getOtherAccess(name)); 1124 notEqNull.setRightOperand(fAst.newNullLiteral()); 1125 1126 IfStatement thenPart= fAst.newIfStatement(); 1127 thenPart.setExpression(notEqNull); 1128 thenPart.setThenStatement(getThenStatement(getReturnFalse())); 1129 1130 Block thenPart2= fAst.newBlock(); 1131 thenPart2.statements().add(thenPart); 1132 1133 // ELSE 1134 MethodInvocation invoc= fAst.newMethodInvocation(); 1135 invoc.setName(fAst.newSimpleName(METHODNAME_EQUALS)); 1136 invoc.setExpression(getThisAccessForEquals(name)); 1137 invoc.arguments().add(getOtherAccess(name)); 1138 1139 PrefixExpression pe= fAst.newPrefixExpression(); 1140 pe.setOperator(PrefixExpression.Operator.NOT); 1141 pe.setOperand(invoc); 1142 1143 IfStatement elsePart= fAst.newIfStatement(); 1144 elsePart.setExpression(pe); 1145 elsePart.setThenStatement(getThenStatement(getReturnFalse())); 1146 1147 // ALL 1148 IfStatement isNull= fAst.newIfStatement(); 1149 isNull.setExpression(newCondition); 1150 isNull.setThenStatement(thenPart2); 1151 isNull.setElseStatement(elsePart); 1152 1153 return isNull; 1154 } 1155 1156 // ************************ HELPERS ************************** 1157 createReturningIfStatement(Expression left, Expression right, Operator operator, boolean whatToReturn)1158 private Statement createReturningIfStatement(Expression left, Expression right, Operator operator, boolean whatToReturn) { 1159 InfixExpression newCondition= fAst.newInfixExpression(); 1160 newCondition.setOperator(operator); 1161 newCondition.setLeftOperand(left); 1162 newCondition.setRightOperand(right); 1163 return createReturningIfStatement(whatToReturn, newCondition); 1164 } 1165 createReturningIfStatement(boolean result, Expression condition)1166 private Statement createReturningIfStatement(boolean result, Expression condition) { 1167 IfStatement firstIf= fAst.newIfStatement(); 1168 firstIf.setExpression(condition); 1169 1170 ReturnStatement returner= fAst.newReturnStatement(); 1171 returner.setExpression(fAst.newBooleanLiteral(result)); 1172 firstIf.setThenStatement(getThenStatement(returner)); 1173 return firstIf; 1174 } 1175 createMethodComment(MethodDeclaration newDeclaration, IMethodBinding copyFrom)1176 private void createMethodComment(MethodDeclaration newDeclaration, IMethodBinding copyFrom) throws CoreException { 1177 if (fSettings.createComments) { 1178 String string= CodeGeneration.getMethodComment(fRewrite.getCu(), fType.getQualifiedName(), newDeclaration, copyFrom, StubUtility.getLineDelimiterUsed(fRewrite.getCu())); 1179 if (string != null) { 1180 Javadoc javadoc= (Javadoc) fRewrite.getASTRewrite().createStringPlaceholder(string, ASTNode.JAVADOC); 1181 newDeclaration.setJavadoc(javadoc); 1182 } 1183 } 1184 IJavaProject project= fUnit.getJavaElement().getJavaProject(); 1185 StubUtility2Core.addOverrideAnnotation(fSettings, project, fRewrite.getASTRewrite(), fRewrite.getImportRewrite(), newDeclaration, copyFrom.getDeclaringClass().isInterface(), null); 1186 } 1187 needsNoSuperCall(ITypeBinding typeBinding, String name, ITypeBinding[] parameters)1188 private boolean needsNoSuperCall(ITypeBinding typeBinding, String name, ITypeBinding[] parameters) { 1189 Assert.isNotNull(typeBinding); 1190 IMethodBinding binding= Bindings.findMethodInHierarchy(typeBinding.getSuperclass(), name, parameters); 1191 if (binding != null && !Modifier.isAbstract(binding.getModifiers())) { 1192 ITypeBinding declaring= binding.getDeclaringClass(); 1193 return declaring.getQualifiedName().equals(JAVA_LANG_OBJECT); 1194 } 1195 return true; 1196 } 1197 1198 getThisAccessForEquals(String name)1199 private Expression getThisAccessForEquals(String name) { 1200 return getThisAccess(name, false); 1201 } 1202 getThisAccessForHashCode(String name)1203 private Expression getThisAccessForHashCode(String name) { 1204 return getThisAccess(name, true); 1205 } 1206 getThisAccess(String name, boolean forHashCode)1207 private Expression getThisAccess(String name, boolean forHashCode) { 1208 if (fSettings.useKeywordThis || needsThisQualification(name, forHashCode)) { 1209 FieldAccess fa= fAst.newFieldAccess(); 1210 fa.setExpression(fAst.newThisExpression()); 1211 fa.setName(fAst.newSimpleName(name)); 1212 return fa; 1213 } 1214 return fAst.newSimpleName(name); 1215 } 1216 getOtherAccess(String name)1217 private Expression getOtherAccess(String name) { 1218 return fAst.newQualifiedName(fAst.newSimpleName(VARIABLE_NAME_EQUALS_CASTED), fAst.newSimpleName(name)); 1219 } 1220 isPrimitiveType(ITypeBinding binding, PrimitiveType.Code code)1221 private boolean isPrimitiveType(ITypeBinding binding, PrimitiveType.Code code) { 1222 return (binding.getName().equals(code.toString())); 1223 } 1224 isPrimitiveType(ITypeBinding type, PrimitiveType.Code[] codes)1225 private boolean isPrimitiveType(ITypeBinding type, PrimitiveType.Code[] codes) { 1226 for (Code code : codes) { 1227 if (isPrimitiveType(type, code)) 1228 return true; 1229 } 1230 return false; 1231 } 1232 getQualifiedName(String name)1233 private Name getQualifiedName(String name) { 1234 String importedType= fRewrite.getImportRewrite().addImport(name, fImportRewriteContext); 1235 return ASTNodeFactory.newName(fAst, importedType); 1236 } 1237 getReturnFalse()1238 private ReturnStatement getReturnFalse() { 1239 ReturnStatement falseReturn= fAst.newReturnStatement(); 1240 falseReturn.setExpression(fAst.newBooleanLiteral(false)); 1241 return falseReturn; 1242 } 1243 getThenStatement(Statement statement)1244 private Statement getThenStatement(Statement statement) { 1245 if (fUseBlocksForThen && !(statement instanceof Block)) { 1246 Block block= fAst.newBlock(); 1247 block.statements().add(statement); 1248 return block; 1249 } 1250 return statement; 1251 } 1252 1253 parenthesize(Expression expression)1254 private Expression parenthesize(Expression expression) { 1255 ParenthesizedExpression pe= fAst.newParenthesizedExpression(); 1256 pe.setExpression(expression); 1257 return pe; 1258 } 1259 createFloatInvocation(Expression access)1260 private Expression createFloatInvocation(Expression access) { 1261 return createMethodInvocation(access, "java.lang.Float", "floatToIntBits"); //$NON-NLS-1$ //$NON-NLS-2$ 1262 } 1263 createDoubleInvocation(Expression access)1264 private Expression createDoubleInvocation(Expression access) { 1265 return createMethodInvocation(access, "java.lang.Double", "doubleToLongBits"); //$NON-NLS-1$ //$NON-NLS-2$ 1266 } 1267 createMethodInvocation(Expression access, String qualifiedClassName, String methodName)1268 private Expression createMethodInvocation(Expression access, String qualifiedClassName, String methodName) { 1269 MethodInvocation invoc= fAst.newMethodInvocation(); 1270 invoc.setExpression(getQualifiedName(qualifiedClassName)); 1271 invoc.setName(fAst.newSimpleName(methodName)); 1272 invoc.arguments().add(access); 1273 return invoc; 1274 } 1275 needsThisQualification(String name, boolean isHashCode)1276 private boolean needsThisQualification(String name, boolean isHashCode) { 1277 if (isHashCode) 1278 return ( (fDoubleCount > 0 && name.equals(VARIABLE_NAME_DOUBLE_TEMPORARY)) || (name.equals(VARIABLE_NAME_PRIME)) || (name 1279 .equals(VARIABLE_NAME_RESULT))); 1280 return ( (name.equals(VARIABLE_NAME_EQUALS_CASTED)) || (name.equals(VARIABLE_NAME_EQUALS_PARAM))); 1281 } 1282 needsDeepMethod(ITypeBinding type)1283 private boolean needsDeepMethod(ITypeBinding type) { 1284 String elementTypeName= type.getErasure().getElementType().getName(); 1285 return type.getDimensions() > 1 || TYPE_NAME_CLONEABLE.equals(elementTypeName) || TYPE_NAME_SERIALIZABLE.equals(elementTypeName) || TYPE_NAME_OBJECT.equals(elementTypeName); 1286 } 1287 1288 } 1289