1 11 package org.eclipse.jdt.internal.corext.codemanipulation; 12 13 import java.util.ArrayList ; 14 import java.util.HashSet ; 15 import java.util.Iterator ; 16 import java.util.List ; 17 import java.util.Set ; 18 19 import org.eclipse.text.edits.TextEdit; 20 21 import org.eclipse.core.runtime.Assert; 22 import org.eclipse.core.runtime.CoreException; 23 import org.eclipse.core.runtime.IProgressMonitor; 24 import org.eclipse.core.runtime.IStatus; 25 import org.eclipse.core.runtime.NullProgressMonitor; 26 import org.eclipse.core.runtime.OperationCanceledException; 27 import org.eclipse.core.runtime.Status; 28 import org.eclipse.core.runtime.SubProgressMonitor; 29 import org.eclipse.core.runtime.jobs.ISchedulingRule; 30 31 import org.eclipse.core.filebuffers.ITextFileBuffer; 32 33 import org.eclipse.core.resources.IWorkspaceRunnable; 34 import org.eclipse.core.resources.ResourcesPlugin; 35 36 import org.eclipse.jface.text.Document; 37 import org.eclipse.jface.text.IDocument; 38 39 import org.eclipse.ltk.core.refactoring.Change; 40 41 import org.eclipse.jdt.core.ICompilationUnit; 42 import org.eclipse.jdt.core.IJavaElement; 43 import org.eclipse.jdt.core.IMethod; 44 import org.eclipse.jdt.core.dom.AST; 45 import org.eclipse.jdt.core.dom.ASTNode; 46 import org.eclipse.jdt.core.dom.AbstractTypeDeclaration; 47 import org.eclipse.jdt.core.dom.ArrayAccess; 48 import org.eclipse.jdt.core.dom.Assignment; 49 import org.eclipse.jdt.core.dom.Block; 50 import org.eclipse.jdt.core.dom.BodyDeclaration; 51 import org.eclipse.jdt.core.dom.CastExpression; 52 import org.eclipse.jdt.core.dom.CompilationUnit; 53 import org.eclipse.jdt.core.dom.ConditionalExpression; 54 import org.eclipse.jdt.core.dom.Expression; 55 import org.eclipse.jdt.core.dom.FieldAccess; 56 import org.eclipse.jdt.core.dom.ForStatement; 57 import org.eclipse.jdt.core.dom.IMethodBinding; 58 import org.eclipse.jdt.core.dom.ITypeBinding; 59 import org.eclipse.jdt.core.dom.IVariableBinding; 60 import org.eclipse.jdt.core.dom.IfStatement; 61 import org.eclipse.jdt.core.dom.InfixExpression; 62 import org.eclipse.jdt.core.dom.InstanceofExpression; 63 import org.eclipse.jdt.core.dom.Javadoc; 64 import org.eclipse.jdt.core.dom.MethodDeclaration; 65 import org.eclipse.jdt.core.dom.MethodInvocation; 66 import org.eclipse.jdt.core.dom.Modifier; 67 import org.eclipse.jdt.core.dom.Name; 68 import org.eclipse.jdt.core.dom.ParenthesizedExpression; 69 import org.eclipse.jdt.core.dom.PostfixExpression; 70 import org.eclipse.jdt.core.dom.PrefixExpression; 71 import org.eclipse.jdt.core.dom.PrimitiveType; 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.VariableDeclarationExpression; 79 import org.eclipse.jdt.core.dom.VariableDeclarationFragment; 80 import org.eclipse.jdt.core.dom.VariableDeclarationStatement; 81 import org.eclipse.jdt.core.dom.InfixExpression.Operator; 82 import org.eclipse.jdt.core.dom.Modifier.ModifierKeyword; 83 import org.eclipse.jdt.core.dom.rewrite.ListRewrite; 84 85 import org.eclipse.jdt.internal.corext.dom.ASTNodeFactory; 86 import org.eclipse.jdt.internal.corext.dom.ASTNodes; 87 import org.eclipse.jdt.internal.corext.dom.Bindings; 88 import org.eclipse.jdt.internal.corext.dom.NodeFinder; 89 import org.eclipse.jdt.internal.corext.refactoring.changes.CompilationUnitChange; 90 import org.eclipse.jdt.internal.corext.refactoring.structure.CompilationUnitRewrite; 91 import org.eclipse.jdt.internal.corext.refactoring.util.RefactoringFileBuffers; 92 import org.eclipse.jdt.internal.corext.util.JavaModelUtil; 93 94 import org.eclipse.jdt.ui.CodeGeneration; 95 96 import org.eclipse.jdt.internal.ui.JavaPlugin; 97 98 138 public final class GenerateHashCodeEqualsOperation implements IWorkspaceRunnable { 139 140 private interface IHashCodeAccessProvider { 141 142 public Expression getThisAccess(String name); 143 } 144 145 private static final String JAVA_UTIL_ARRAYS= "java.util.Arrays"; 147 private static final String BOOLEAN_TRUE_CONSTANT= "1231"; 149 private static final String BOOLEAN_FALSE_CONSTANT= "1237"; 151 private static final String JAVA_LANG_OBJECT= "java.lang.Object"; 153 private static final String METHODNAME_GETCLASS= "getClass"; 155 private static final String METHODNAME_EQUALS= "equals"; 157 private static final String METHODNAME_HASH_CODE= "hashCode"; 159 private static final String PRIME_NUMBER= "31"; 161 private static final String INITIAL_HASHCODE_VALUE= "1"; 163 private static final String VARIABLE_NAME_DOUBLE_TEMPORARY= "temp"; 165 private static final String VARIABLE_NAME_PRIME= "prime"; 167 private static final String VARIABLE_NAME_RESULT= "result"; 169 private static final String VARIABLE_NAME_EQUALS_PARAM= "obj"; 171 private static final String VARIABLE_NAME_HASHCODE_PARAM= "array"; 173 private static final String VARIABLE_NAME_EQUALS_CASTED= "other"; 175 private static final String VARIABLE_NAME_INDEX= "index"; 177 178 private final boolean fApply; 179 180 181 private TextEdit fEdit= null; 182 183 184 private final IJavaElement fInsert; 185 186 187 private final IVariableBinding[] fFields; 188 189 190 private final boolean fForce; 191 192 193 private final boolean fSave; 194 195 196 private final CodeGenerationSettings fSettings; 197 198 199 private final ITypeBinding fType; 200 201 202 private final CompilationUnit fUnit; 203 204 205 private final CompilationUnitRewrite fRewrite; 206 207 208 private final AST fAst; 209 210 211 private int fDoubleCount; 212 213 214 private Set fCustomHashCodeTypes= new HashSet (); 215 216 217 private final boolean fUseInstanceOf; 218 219 235 public GenerateHashCodeEqualsOperation(final ITypeBinding type, final IVariableBinding[] fields, final CompilationUnit unit, 236 final IJavaElement insert, final CodeGenerationSettings settings, final boolean useInstanceof, final boolean force, final boolean apply, final boolean save) { 237 Assert.isNotNull(type); 238 Assert.isNotNull(fields); 239 Assert.isNotNull(unit); 240 Assert.isNotNull(settings); 241 fType= type; 242 fInsert= insert; 243 fUnit= unit; 244 fFields= fields; 245 fSettings= settings; 246 fUseInstanceOf= useInstanceof; 247 fSave= save; 248 fApply= apply; 249 fDoubleCount= 0; 250 fRewrite= new CompilationUnitRewrite((ICompilationUnit) fUnit.getJavaElement(), fUnit); 251 fForce= force; 252 fAst= fRewrite.getAST(); 253 } 254 255 260 public final TextEdit getResultingEdit() { 261 return fEdit; 262 } 263 264 269 public final ISchedulingRule getSchedulingRule() { 270 return ResourcesPlugin.getWorkspace().getRoot(); 271 } 272 273 276 public final void run(IProgressMonitor monitor) throws CoreException { 277 if (monitor == null) 278 monitor= new NullProgressMonitor(); 279 try { 280 monitor.beginTask("", 1); monitor.setTaskName(CodeGenerationMessages.GenerateHashCodeEqualsOperation_description); 282 283 fCustomHashCodeTypes.clear(); 284 285 AbstractTypeDeclaration declaration= (AbstractTypeDeclaration) ASTNodes.findDeclaration(fType, fRewrite.getRoot()); 287 ListRewrite rewriter= fRewrite.getASTRewrite().getListRewrite(declaration, declaration.getBodyDeclarationsProperty()); 288 289 if (fType != null && rewriter != null) { 290 291 ICompilationUnit cu= (ICompilationUnit) fUnit.getJavaElement(); 292 293 ITextFileBuffer buffer= null; 294 IDocument document= null; 295 try { 296 if (!JavaModelUtil.isPrimary(cu)) 297 document= new Document(cu.getBuffer().getContents()); 298 else { 299 buffer= RefactoringFileBuffers.acquire(cu); 300 document= buffer.getDocument(); 301 } 302 ASTNode insertion= null; 303 if (fInsert instanceof IMethod) 304 insertion= ASTNodes.getParent(NodeFinder.perform(fRewrite.getRoot(), ((IMethod) fInsert).getNameRange()), 305 MethodDeclaration.class); 306 307 BodyDeclaration toReplace= null; 308 if (fForce) { 309 final List list= (List ) declaration.getStructuralProperty(declaration.getBodyDeclarationsProperty()); 310 for (final Iterator iterator= list.iterator(); iterator.hasNext();) { 311 final BodyDeclaration bodyDecl= (BodyDeclaration) iterator.next(); 312 if (bodyDecl instanceof MethodDeclaration) { 313 final MethodDeclaration method= (MethodDeclaration) bodyDecl; 314 final IMethodBinding binding= method.resolveBinding(); 315 if (binding != null && binding.getName().equals(METHODNAME_EQUALS)) { 316 final ITypeBinding[] bindings= binding.getParameterTypes(); 317 if (bindings.length == 1 && bindings[0].getQualifiedName().equals(JAVA_LANG_OBJECT)) { 318 toReplace= bodyDecl; 319 break; 320 } 321 } 322 } 323 } 324 } 325 MethodDeclaration equalsMethod= createEqualsMethod(); 327 addMethod(rewriter, insertion, equalsMethod, toReplace); 328 329 if (monitor.isCanceled()) 330 throw new OperationCanceledException(); 331 332 toReplace= null; 333 if (fForce) { 334 final List list= (List ) declaration.getStructuralProperty(declaration.getBodyDeclarationsProperty()); 335 for (final Iterator iterator= list.iterator(); iterator.hasNext();) { 336 final BodyDeclaration bodyDecl= (BodyDeclaration) iterator.next(); 337 if (bodyDecl instanceof MethodDeclaration) { 338 final MethodDeclaration method= (MethodDeclaration) bodyDecl; 339 final IMethodBinding binding= method.resolveBinding(); 340 if (binding != null && binding.getName().equals(METHODNAME_HASH_CODE)) { 341 final ITypeBinding[] bindings= binding.getParameterTypes(); 342 if (bindings.length == 0) { 343 toReplace= bodyDecl; 344 break; 345 } 346 } 347 } 348 } 349 } 350 MethodDeclaration hashCodeMethod= createHashCodeMethod(); 352 addMethod(rewriter, equalsMethod, hashCodeMethod, toReplace); 353 354 MethodDeclaration previous= null; 356 for (final Iterator iterator= fCustomHashCodeTypes.iterator(); iterator.hasNext();) { 357 boolean found= false; 358 final ITypeBinding binding= (ITypeBinding) iterator.next(); 359 final ITypeBinding typeBinding= declaration.resolveBinding(); 360 if (typeBinding != null) { 361 final IMethodBinding[] bindings= typeBinding.getDeclaredMethods(); 362 for (int index= 0; index < bindings.length; index++) { 363 final IMethodBinding method= bindings[index]; 364 final ITypeBinding[] parameters= method.getParameterTypes(); 365 if (method.getName().equals(METHODNAME_HASH_CODE) && parameters.length == 1) { 366 final ITypeBinding parameter= parameters[0]; 367 if (!parameter.isPrimitive()) { 368 found= parameter.getQualifiedName().equals(JAVA_LANG_OBJECT); 369 } else 370 found= parameter.isEqualTo(binding); 371 if (found) 372 break; 373 } 374 } 375 } 376 if (!found) { 377 final MethodDeclaration helperDecl= createHashCodeHelper(binding); 378 addHelper(rewriter, previous, helperDecl); 379 } 380 } 381 382 final Change result= fRewrite.createChange(); 384 if (result instanceof CompilationUnitChange) { 385 final CompilationUnitChange change= (CompilationUnitChange) result; 386 final TextEdit edit= change.getEdit(); 387 if (edit != null) { 388 try { 389 fEdit= edit; 390 if (fApply) 391 edit.apply(document, TextEdit.UPDATE_REGIONS); 392 if (fSave) { 393 if (buffer != null) 394 buffer.commit(new SubProgressMonitor(monitor, 1), true); 395 else 396 cu.getBuffer().setContents(document.get()); 397 } 398 } catch (Exception exception) { 399 throw new CoreException(new Status(IStatus.ERROR, JavaPlugin.getPluginId(), 0, exception.getLocalizedMessage(), 400 exception)); 401 } 402 } 403 } 404 } finally { 405 if (buffer != null) 406 RefactoringFileBuffers.release(cu); 407 } 408 } 409 } finally { 410 monitor.done(); 411 } 412 } 413 414 private void addHelper(ListRewrite rewriter, ASTNode insertion, MethodDeclaration stub) { 415 if (insertion != null) 416 rewriter.insertBefore(stub, insertion, null); 417 else 418 rewriter.insertFirst(stub, null); 419 } 420 421 private void addMethod(ListRewrite rewriter, ASTNode insertion, MethodDeclaration stub, BodyDeclaration replace) { 422 if (replace != null) { 423 rewriter.replace(replace, stub, null); 424 } else { 425 if (insertion != null) 426 rewriter.insertBefore(stub, insertion, null); 427 else 428 rewriter.insertLast(stub, null); 429 } 430 } 431 432 434 private MethodDeclaration createHashCodeMethod() throws CoreException { 435 436 MethodDeclaration hashCodeMethod= fAst.newMethodDeclaration(); 437 hashCodeMethod.modifiers().addAll(ASTNodeFactory.newModifiers(fAst, Modifier.PUBLIC)); 438 hashCodeMethod.setName(fAst.newSimpleName(METHODNAME_HASH_CODE)); 439 hashCodeMethod.setConstructor(false); 440 hashCodeMethod.setReturnType2(fAst.newPrimitiveType(PrimitiveType.INT)); 441 442 Block body= fAst.newBlock(); 443 hashCodeMethod.setBody(body); 444 445 VariableDeclarationFragment frag= fAst.newVariableDeclarationFragment(); 447 frag.setName(fAst.newSimpleName(VARIABLE_NAME_PRIME)); 448 frag.setInitializer(fAst.newNumberLiteral(PRIME_NUMBER)); 449 450 VariableDeclarationStatement primeNumberDeclaration= fAst.newVariableDeclarationStatement(frag); 451 primeNumberDeclaration.modifiers().add(fAst.newModifier(ModifierKeyword.FINAL_KEYWORD)); 452 primeNumberDeclaration.setType(fAst.newPrimitiveType(PrimitiveType.INT)); 453 body.statements().add(primeNumberDeclaration); 454 455 VariableDeclarationFragment fragment= fAst.newVariableDeclarationFragment(); 457 fragment.setName(fAst.newSimpleName(VARIABLE_NAME_RESULT)); 458 459 VariableDeclarationStatement resultDeclaration= fAst.newVariableDeclarationStatement(fragment); 460 resultDeclaration.setType(fAst.newPrimitiveType(PrimitiveType.INT)); 461 body.statements().add(resultDeclaration); 462 463 if (needsNoSuperCall(fType, METHODNAME_HASH_CODE, new ITypeBinding[0])) { 464 fragment.setInitializer(fAst.newNumberLiteral(INITIAL_HASHCODE_VALUE)); 465 } else { 466 SuperMethodInvocation invoc= fAst.newSuperMethodInvocation(); 467 invoc.setName(fAst.newSimpleName(METHODNAME_HASH_CODE)); 468 fragment.setInitializer(invoc); 469 } 470 471 for (int i= 0; i < fFields.length; i++) { 472 if (fFields[i].getType().isPrimitive()) { 473 Statement[] sts= createAddSimpleHashCode(fFields[i].getType(), new IHashCodeAccessProvider() { 474 475 public Expression getThisAccess(String name) { 476 return getThisAccessForHashCode(name); 477 } 478 479 }, fFields[i].getName(), false); 480 for (int j= 0; j < sts.length; j++) { 481 body.statements().add(sts[j]); 482 } 483 } else if (fFields[i].getType().isArray()) 484 body.statements().add(createAddArrayHashCode(fFields[i])); 485 else 486 body.statements().add(createAddQualifiedHashCode(fFields[i])); 487 } 488 489 ReturnStatement endReturn= fAst.newReturnStatement(); 491 endReturn.setExpression(fAst.newSimpleName(VARIABLE_NAME_RESULT)); 492 body.statements().add(endReturn); 493 494 if (fSettings != null) { 496 ITypeBinding object= fAst.resolveWellKnownType(JAVA_LANG_OBJECT); 497 IMethodBinding[] objms= object.getDeclaredMethods(); 498 IMethodBinding objectMethod= null; 499 for (int i= 0; i < objms.length; i++) { 500 if (objms[i].getName().equals(METHODNAME_HASH_CODE) && objms[i].getParameterTypes().length == 0) 501 objectMethod= objms[i]; 502 } 503 createMethodComment(hashCodeMethod, objectMethod); 504 } 505 506 return hashCodeMethod; 507 } 508 509 510 private Statement[] createAddSimpleHashCode(ITypeBinding type, IHashCodeAccessProvider provider, String name, boolean singleTemp) { 511 512 List statements= new ArrayList (); 513 514 if (!type.isPrimitive()) { 515 ConditionalExpression ce= fAst.newConditionalExpression(); 517 InfixExpression exp= fAst.newInfixExpression(); 518 ArrayAccess access= fAst.newArrayAccess(); 519 access.setArray(fAst.newSimpleName(VARIABLE_NAME_HASHCODE_PARAM)); 520 access.setIndex(fAst.newSimpleName(VARIABLE_NAME_INDEX)); 521 exp.setLeftOperand(access); 522 exp.setOperator(Operator.EQUALS); 523 exp.setRightOperand(fAst.newNullLiteral()); 524 ce.setExpression(exp); 525 ce.setThenExpression(fAst.newNumberLiteral("0")); MethodInvocation invoc= fAst.newMethodInvocation(); 527 access= fAst.newArrayAccess(); 528 access.setArray(fAst.newSimpleName(VARIABLE_NAME_HASHCODE_PARAM)); 529 access.setIndex(fAst.newSimpleName(VARIABLE_NAME_INDEX)); 530 invoc.setExpression(access); 531 invoc.setName(fAst.newSimpleName(METHODNAME_HASH_CODE)); 532 ce.setElseExpression(invoc); 533 statements.add(prepareAssignment(parenthesize(ce))); 534 } else if (isPrimitiveType(type, PrimitiveType.BOOLEAN)) { 535 ConditionalExpression ce= fAst.newConditionalExpression(); 536 ce.setExpression(provider.getThisAccess(name)); 537 ce.setThenExpression(fAst.newNumberLiteral(BOOLEAN_TRUE_CONSTANT)); 539 ce.setElseExpression(fAst.newNumberLiteral(BOOLEAN_FALSE_CONSTANT)); 540 statements.add(prepareAssignment(parenthesize(ce))); 541 } else if (isPrimitiveType(type, new PrimitiveType.Code[] { PrimitiveType.CHAR, PrimitiveType.INT, PrimitiveType.SHORT })) { 542 statements.add(prepareAssignment(provider.getThisAccess(name))); 543 } else if (isPrimitiveType(type, PrimitiveType.FLOAT)) { 544 statements.add(prepareAssignment(createFloatInvocation(provider.getThisAccess(name)))); 546 } else if (isPrimitiveType(type, PrimitiveType.LONG)) { 547 statements.add(prepareAssignment(createShiftAssignment(provider.getThisAccess(name), provider.getThisAccess(name)))); 548 } else if (isPrimitiveType(type, PrimitiveType.DOUBLE)) { 549 550 VariableDeclarationFragment fragment= null; 551 if (singleTemp || fDoubleCount == 0) { 552 fragment= fAst.newVariableDeclarationFragment(); 553 fragment.setName(fAst.newSimpleName(VARIABLE_NAME_DOUBLE_TEMPORARY)); 554 555 VariableDeclarationStatement st2= fAst.newVariableDeclarationStatement(fragment); 556 st2.setType(fAst.newPrimitiveType(PrimitiveType.LONG)); 557 statements.add(st2); 558 } 559 fDoubleCount++; 560 561 Expression comparison= createDoubleInvocation(provider.getThisAccess(name)); 563 564 if (singleTemp) 565 fragment.setInitializer(comparison); 566 else { 567 Assignment ass= fAst.newAssignment(); 568 ass.setLeftHandSide(fAst.newSimpleName(VARIABLE_NAME_DOUBLE_TEMPORARY)); 569 ass.setRightHandSide(comparison); 570 statements.add(fAst.newExpressionStatement(ass)); 571 } 572 statements.add(prepareAssignment(createShiftAssignment(fAst.newSimpleName(VARIABLE_NAME_DOUBLE_TEMPORARY), fAst.newSimpleName(VARIABLE_NAME_DOUBLE_TEMPORARY)))); 573 } 574 575 return (Statement[]) statements.toArray(new Statement[statements.size()]); 576 } 577 578 private Statement createAddArrayHashCode(IVariableBinding binding) { 579 MethodInvocation invoc= fAst.newMethodInvocation(); 580 if (JavaModelUtil.is50OrHigher(fRewrite.getCu().getJavaProject())) { 581 invoc.setName(fAst.newSimpleName(METHODNAME_HASH_CODE)); 582 invoc.setExpression(getQualifiedName(JAVA_UTIL_ARRAYS)); 583 invoc.arguments().add(getThisAccessForHashCode(binding.getName())); 584 } else { 585 invoc.setName(fAst.newSimpleName(METHODNAME_HASH_CODE)); 586 final IJavaElement element= fType.getJavaElement(); 587 if (element != null && !"".equals(element.getElementName())) invoc.setExpression(fAst.newSimpleName(element.getElementName())); 589 invoc.arguments().add(getThisAccessForHashCode(binding.getName())); 590 final ITypeBinding type= binding.getType().getElementType(); 591 if (!Bindings.isVoidType(type)) { 592 if (type.isPrimitive() && binding.getType().getDimensions() < 2) 593 fCustomHashCodeTypes.add(type); 594 else 595 fCustomHashCodeTypes.add(fAst.resolveWellKnownType(JAVA_LANG_OBJECT)); 596 } 597 } 598 return prepareAssignment(invoc); 599 } 600 601 private MethodDeclaration createHashCodeHelper(ITypeBinding binding) { 602 Assert.isTrue(!binding.isArray()); 603 604 MethodDeclaration hashCodeMethod= fAst.newMethodDeclaration(); 605 hashCodeMethod.modifiers().addAll(ASTNodeFactory.newModifiers(fAst, Modifier.PRIVATE | Modifier.STATIC)); 606 hashCodeMethod.setName(fAst.newSimpleName(METHODNAME_HASH_CODE)); 607 hashCodeMethod.setConstructor(false); 608 hashCodeMethod.setReturnType2(fAst.newPrimitiveType(PrimitiveType.INT)); 609 610 List parameters= hashCodeMethod.parameters(); 612 SingleVariableDeclaration hashCodeParam= fAst.newSingleVariableDeclaration(); 613 if (!binding.isPrimitive()) 614 hashCodeParam.setType(fAst.newArrayType(fAst.newSimpleType(getQualifiedName(JAVA_LANG_OBJECT)), 1)); 615 else 616 hashCodeParam.setType(fAst.newArrayType(fAst.newPrimitiveType(PrimitiveType.toCode(binding.getName())), 1)); 617 hashCodeParam.setName(fAst.newSimpleName(VARIABLE_NAME_HASHCODE_PARAM)); 618 parameters.add(hashCodeParam); 619 620 Block body= fAst.newBlock(); 621 hashCodeMethod.setBody(body); 622 623 VariableDeclarationFragment frag= fAst.newVariableDeclarationFragment(); 625 frag.setName(fAst.newSimpleName(VARIABLE_NAME_PRIME)); 626 frag.setInitializer(fAst.newNumberLiteral(PRIME_NUMBER)); 627 628 VariableDeclarationStatement primeNumberDeclaration= fAst.newVariableDeclarationStatement(frag); 629 primeNumberDeclaration.modifiers().add(fAst.newModifier(ModifierKeyword.FINAL_KEYWORD)); 630 primeNumberDeclaration.setType(fAst.newPrimitiveType(PrimitiveType.INT)); 631 body.statements().add(primeNumberDeclaration); 632 633 IfStatement ifStatement= fAst.newIfStatement(); 635 final InfixExpression newInfixExpression= fAst.newInfixExpression(); 636 newInfixExpression.setLeftOperand(fAst.newSimpleName(VARIABLE_NAME_HASHCODE_PARAM)); 637 newInfixExpression.setRightOperand(fAst.newNullLiteral()); 638 newInfixExpression.setOperator(Operator.EQUALS); 639 ifStatement.setExpression(newInfixExpression); 640 final ReturnStatement returnStatement= fAst.newReturnStatement(); 641 returnStatement.setExpression(fAst.newNumberLiteral("0")); ifStatement.setThenStatement(returnStatement); 643 body.statements().add(ifStatement); 644 645 VariableDeclarationFragment resultFragment= fAst.newVariableDeclarationFragment(); 647 resultFragment.setName(fAst.newSimpleName(VARIABLE_NAME_RESULT)); 648 resultFragment.setInitializer(fAst.newNumberLiteral(INITIAL_HASHCODE_VALUE)); 649 VariableDeclarationStatement resultDeclaration= fAst.newVariableDeclarationStatement(resultFragment); 650 resultDeclaration.setType(fAst.newPrimitiveType(PrimitiveType.INT)); 651 body.statements().add(resultDeclaration); 652 653 ForStatement forStatement= fAst.newForStatement(); 655 656 VariableDeclarationFragment indexDeclaration= fAst.newVariableDeclarationFragment(); 657 indexDeclaration.setName(fAst.newSimpleName(VARIABLE_NAME_INDEX)); 658 indexDeclaration.setInitializer(fAst.newNumberLiteral("0")); final VariableDeclarationExpression declExpression= fAst.newVariableDeclarationExpression(indexDeclaration); 660 declExpression.setType(fAst.newPrimitiveType(PrimitiveType.INT)); 661 forStatement.initializers().add(declExpression); 662 InfixExpression infixExpr= fAst.newInfixExpression(); 663 infixExpr.setLeftOperand(fAst.newSimpleName(VARIABLE_NAME_INDEX)); 664 FieldAccess access= fAst.newFieldAccess(); 665 access.setExpression(fAst.newSimpleName(VARIABLE_NAME_HASHCODE_PARAM)); 666 access.setName(fAst.newSimpleName("length")); infixExpr.setRightOperand(access); 668 infixExpr.setOperator(Operator.LESS); 669 forStatement.setExpression(infixExpr); 670 PostfixExpression postfixExpr= fAst.newPostfixExpression(); 671 postfixExpr.setOperand(fAst.newSimpleName(VARIABLE_NAME_INDEX)); 672 postfixExpr.setOperator(org.eclipse.jdt.core.dom.PostfixExpression.Operator.INCREMENT); 673 forStatement.updaters().add(postfixExpr); 674 body.statements().add(forStatement); 675 676 Block forBody= fAst.newBlock(); 677 Statement[] statements= createAddSimpleHashCode(binding, new IHashCodeAccessProvider() { 678 679 public Expression getThisAccess(String name) { 680 ArrayAccess a= fAst.newArrayAccess(); 681 a.setArray(fAst.newSimpleName(VARIABLE_NAME_HASHCODE_PARAM)); 682 a.setIndex(fAst.newSimpleName(name)); 683 return a; 684 } 685 }, VARIABLE_NAME_INDEX, true); 686 for (int index= 0; index < statements.length; index++) { 687 forBody.statements().add(statements[index]); 688 } 689 forStatement.setBody(forBody); 690 691 ReturnStatement endReturn= fAst.newReturnStatement(); 693 endReturn.setExpression(fAst.newSimpleName(VARIABLE_NAME_RESULT)); 694 body.statements().add(endReturn); 695 696 if (fSettings != null && fSettings.createComments) { 698 Javadoc javadoc= fAst.newJavadoc(); 699 final TagElement tagComment= fAst.newTagElement(); 700 TextElement text= fAst.newTextElement(); 701 text.setText(CodeGenerationMessages.GenerateHashCodeEqualsOperation_hash_code_comment); 702 tagComment.fragments().add(text); 703 javadoc.tags().add(tagComment); 704 final TagElement tagParam= fAst.newTagElement(); 705 tagParam.setTagName(CodeGenerationMessages.GenerateHashCodeEqualsOperation_tag_param); 706 tagParam.fragments().add(fAst.newSimpleName(VARIABLE_NAME_HASHCODE_PARAM)); 707 text= fAst.newTextElement(); 708 text.setText(CodeGenerationMessages.GenerateHashCodeEqualsOperation_hash_code_argument); 709 tagParam.fragments().add(text); 710 javadoc.tags().add(tagParam); 711 final TagElement tagReturn= fAst.newTagElement(); 712 tagReturn.setTagName(CodeGenerationMessages.GenerateHashCodeEqualsOperation_tag_return); 713 text= fAst.newTextElement(); 714 text.setText(CodeGenerationMessages.GenerateHashCodeEqualsOperation_return_comment); 715 tagReturn.fragments().add(text); 716 javadoc.tags().add(tagReturn); 717 hashCodeMethod.setJavadoc(javadoc); 718 } 719 return hashCodeMethod; 720 } 721 722 private Statement createAddQualifiedHashCode(IVariableBinding binding) { 723 724 MethodInvocation invoc= fAst.newMethodInvocation(); 725 invoc.setExpression(getThisAccessForHashCode(binding.getName())); 726 invoc.setName(fAst.newSimpleName(METHODNAME_HASH_CODE)); 727 728 InfixExpression expr= fAst.newInfixExpression(); 729 expr.setOperator(Operator.EQUALS); 730 expr.setLeftOperand(getThisAccessForHashCode(binding.getName())); 731 expr.setRightOperand(fAst.newNullLiteral()); 732 733 ConditionalExpression cexpr= fAst.newConditionalExpression(); 734 cexpr.setThenExpression(fAst.newNumberLiteral("0")); cexpr.setElseExpression(invoc); 736 cexpr.setExpression(parenthesize(expr)); 737 738 return prepareAssignment(parenthesize(cexpr)); 739 } 740 741 private Expression createShiftAssignment(Expression shift1, Expression shift2) { 742 CastExpression ce= fAst.newCastExpression(); 746 ce.setType(fAst.newPrimitiveType(PrimitiveType.INT)); 747 748 InfixExpression unsignedShiftRight= fAst.newInfixExpression(); 749 unsignedShiftRight.setLeftOperand(shift1); 750 unsignedShiftRight.setRightOperand(fAst.newNumberLiteral("32")); unsignedShiftRight.setOperator(Operator.RIGHT_SHIFT_UNSIGNED); 752 753 InfixExpression xor= fAst.newInfixExpression(); 754 xor.setLeftOperand(shift2); 755 xor.setRightOperand(parenthesize(unsignedShiftRight)); 756 xor.setOperator(InfixExpression.Operator.XOR); 757 758 ce.setExpression(parenthesize(xor)); 759 return ce; 760 } 761 762 private Statement prepareAssignment(Expression rightHand) { 763 InfixExpression mul= fAst.newInfixExpression(); 765 mul.setLeftOperand(fAst.newSimpleName(VARIABLE_NAME_PRIME)); 766 mul.setRightOperand(fAst.newSimpleName(VARIABLE_NAME_RESULT)); 767 mul.setOperator(Operator.TIMES); 768 769 Assignment ass= fAst.newAssignment(); 770 ass.setLeftHandSide(fAst.newSimpleName(VARIABLE_NAME_RESULT)); 771 772 InfixExpression plus= fAst.newInfixExpression(); 773 plus.setLeftOperand(mul); 774 plus.setOperator(Operator.PLUS); 775 plus.setRightOperand(rightHand); 776 777 ass.setRightHandSide(plus); 778 779 return fAst.newExpressionStatement(ass); 780 } 781 782 784 private MethodDeclaration createEqualsMethod() throws CoreException { 785 786 MethodDeclaration equalsMethodDeclaration= fAst.newMethodDeclaration(); 787 equalsMethodDeclaration.modifiers().addAll(ASTNodeFactory.newModifiers(fAst, Modifier.PUBLIC)); 788 equalsMethodDeclaration.setName(fAst.newSimpleName(METHODNAME_EQUALS)); 789 equalsMethodDeclaration.setConstructor(false); 790 equalsMethodDeclaration.setReturnType2(fAst.newPrimitiveType(PrimitiveType.BOOLEAN)); 791 792 List parameters= equalsMethodDeclaration.parameters(); 793 SingleVariableDeclaration equalsParam= fAst.newSingleVariableDeclaration(); 794 equalsParam.setType(fAst.newSimpleType(fAst.newSimpleName("Object"))); equalsParam.setName(fAst.newSimpleName(VARIABLE_NAME_EQUALS_PARAM)); 796 parameters.add(equalsParam); 797 798 Block body= fAst.newBlock(); 799 equalsMethodDeclaration.setBody(body); 800 801 body.statements().add( 803 createReturningIfStatement(fAst.newThisExpression(), fAst.newSimpleName(VARIABLE_NAME_EQUALS_PARAM), Operator.EQUALS, true)); 804 805 if (needsNoSuperCall(fType, METHODNAME_EQUALS, new ITypeBinding[] {fAst.resolveWellKnownType(JAVA_LANG_OBJECT)})) { 806 body.statements().add( 808 createReturningIfStatement(fAst.newSimpleName(VARIABLE_NAME_EQUALS_PARAM), fAst.newNullLiteral(), Operator.EQUALS, false)); 809 810 } else { 811 SuperMethodInvocation superEqualsCall= fAst.newSuperMethodInvocation(); 813 superEqualsCall.setName(fAst.newSimpleName(METHODNAME_EQUALS)); 814 superEqualsCall.arguments().add(fAst.newSimpleName(VARIABLE_NAME_EQUALS_PARAM)); 815 816 PrefixExpression pe= fAst.newPrefixExpression(); 817 pe.setOperator(PrefixExpression.Operator.NOT); 818 pe.setOperand(superEqualsCall); 819 820 IfStatement superEqualsIf= fAst.newIfStatement(); 821 superEqualsIf.setExpression(pe); 822 superEqualsIf.setThenStatement(getReturnFalse()); 823 824 body.statements().add(superEqualsIf); 825 } 826 827 if (fUseInstanceOf) { 828 InstanceofExpression expression= fAst.newInstanceofExpression(); 830 expression.setLeftOperand(fAst.newSimpleName(VARIABLE_NAME_EQUALS_PARAM)); 831 expression.setRightOperand(fRewrite.getImportRewrite().addImport(fType, fAst)); 832 833 PrefixExpression notExpression= fAst.newPrefixExpression(); 834 notExpression.setOperator(org.eclipse.jdt.core.dom.PrefixExpression.Operator.NOT); 835 836 ParenthesizedExpression parenthesizedExpression= fAst.newParenthesizedExpression(); 837 parenthesizedExpression.setExpression(expression); 838 839 notExpression.setOperand(parenthesizedExpression); 840 841 body.statements().add(createReturningIfStatement(false, notExpression)); 842 } else { 843 MethodInvocation thisClass= fAst.newMethodInvocation(); 845 thisClass.setName(fAst.newSimpleName(METHODNAME_GETCLASS)); 846 847 MethodInvocation objGetClass= fAst.newMethodInvocation(); 848 objGetClass.setExpression(fAst.newSimpleName(VARIABLE_NAME_EQUALS_PARAM)); 849 objGetClass.setName(fAst.newSimpleName(METHODNAME_GETCLASS)); 850 851 body.statements().add(createReturningIfStatement(thisClass, objGetClass, Operator.NOT_EQUALS, false)); 852 } 853 854 VariableDeclarationFragment sd= fAst.newVariableDeclarationFragment(); 856 sd.setName(fAst.newSimpleName(VARIABLE_NAME_EQUALS_CASTED)); 857 858 CastExpression cast= fAst.newCastExpression(); 859 cast.setType(fAst.newSimpleType(fAst.newSimpleName(fType.getName()))); 860 cast.setExpression(fAst.newSimpleName(VARIABLE_NAME_EQUALS_PARAM)); 861 sd.setInitializer(cast); 862 863 VariableDeclarationStatement otherDeclaration= fAst.newVariableDeclarationStatement(sd); 864 otherDeclaration.setType(fAst.newSimpleType(fAst.newSimpleName(fType.getName()))); 865 otherDeclaration.modifiers().add(fAst.newModifier(ModifierKeyword.FINAL_KEYWORD)); 866 867 body.statements().add(otherDeclaration); 868 869 for (int i= 0; i < fFields.length; i++) { 870 if (fFields[i].getType().isPrimitive()) 871 body.statements().add(createSimpleComparison(fFields[i])); 872 else if (fFields[i].getType().isArray()) 873 body.statements().add(createArrayComparison(fFields[i].getName())); 874 else 875 body.statements().add(createQualifiedComparison(fFields[i].getName())); 876 } 877 878 ReturnStatement endReturn= fAst.newReturnStatement(); 880 endReturn.setExpression(fAst.newBooleanLiteral(true)); 881 882 body.statements().add(endReturn); 883 884 if (fSettings != null) { 886 ITypeBinding object= fAst.resolveWellKnownType(JAVA_LANG_OBJECT); 887 IMethodBinding[] objms= object.getDeclaredMethods(); 888 IMethodBinding objectMethod= null; 889 for (int i= 0; i < objms.length; i++) { 890 if (objms[i].getName().equals(METHODNAME_EQUALS) && objms[i].getParameterTypes().length == 1 891 && objms[i].getParameterTypes()[0].getQualifiedName().equals(JAVA_LANG_OBJECT)) 892 objectMethod= objms[i]; 893 } 894 createMethodComment(equalsMethodDeclaration, objectMethod); 895 } 896 897 return equalsMethodDeclaration; 898 } 899 900 private Statement createSimpleComparison(IVariableBinding binding) { 901 if (isPrimitiveType(binding.getType(), PrimitiveType.FLOAT)) { 902 return createReturningIfStatement(createFloatInvocation(getThisAccessForEquals(binding.getName())), createFloatInvocation(getOtherAccess(binding 903 .getName())), Operator.NOT_EQUALS, false); 904 } else if (isPrimitiveType(binding.getType(), PrimitiveType.DOUBLE)) { 905 return createReturningIfStatement(createDoubleInvocation(getThisAccessForEquals(binding.getName())), createDoubleInvocation(getOtherAccess(binding 906 .getName())), Operator.NOT_EQUALS, false); 907 } else 908 return createReturningIfStatement(getThisAccessForEquals(binding.getName()), getOtherAccess(binding.getName()), Operator.NOT_EQUALS, false); 909 } 910 911 private Statement createArrayComparison(String name) { 912 MethodInvocation invoc= fAst.newMethodInvocation(); 913 invoc.setName(fAst.newSimpleName(METHODNAME_EQUALS)); 914 invoc.setExpression(getQualifiedName(JAVA_UTIL_ARRAYS)); 915 invoc.arguments().add(getThisAccessForEquals(name)); 916 invoc.arguments().add(getOtherAccess(name)); 917 918 PrefixExpression pe= fAst.newPrefixExpression(); 919 pe.setOperator(PrefixExpression.Operator.NOT); 920 pe.setOperand(invoc); 921 922 IfStatement ifSt= fAst.newIfStatement(); 923 ifSt.setExpression(pe); 924 ifSt.setThenStatement(getReturnFalse()); 925 926 return ifSt; 927 } 928 929 944 private Statement createQualifiedComparison(String name) { 945 InfixExpression newCondition= fAst.newInfixExpression(); 946 newCondition.setOperator(Operator.EQUALS); 947 newCondition.setLeftOperand(getThisAccessForEquals(name)); 948 newCondition.setRightOperand(fAst.newNullLiteral()); 949 950 InfixExpression notEqNull= fAst.newInfixExpression(); 952 notEqNull.setOperator(Operator.NOT_EQUALS); 953 notEqNull.setLeftOperand(getOtherAccess(name)); 954 notEqNull.setRightOperand(fAst.newNullLiteral()); 955 956 IfStatement thenPart= fAst.newIfStatement(); 957 thenPart.setExpression(notEqNull); 958 thenPart.setThenStatement(getReturnFalse()); 959 960 Block thenPart2= fAst.newBlock(); 961 thenPart2.statements().add(thenPart); 962 963 MethodInvocation invoc= fAst.newMethodInvocation(); 965 invoc.setName(fAst.newSimpleName(METHODNAME_EQUALS)); 966 invoc.setExpression(getThisAccessForEquals(name)); 967 invoc.arguments().add(getOtherAccess(name)); 968 969 PrefixExpression pe= fAst.newPrefixExpression(); 970 pe.setOperator(PrefixExpression.Operator.NOT); 971 pe.setOperand(invoc); 972 973 IfStatement elsePart= fAst.newIfStatement(); 974 elsePart.setExpression(pe); 975 elsePart.setThenStatement(getReturnFalse()); 976 977 IfStatement isNull= fAst.newIfStatement(); 979 isNull.setExpression(newCondition); 980 isNull.setThenStatement(thenPart2); 981 isNull.setElseStatement(elsePart); 982 983 return isNull; 984 } 985 986 988 private Statement createReturningIfStatement(Expression left, Expression right, Operator operator, boolean whatToReturn) { 989 InfixExpression newCondition= fAst.newInfixExpression(); 990 newCondition.setOperator(operator); 991 newCondition.setLeftOperand(left); 992 newCondition.setRightOperand(right); 993 return createReturningIfStatement(whatToReturn, newCondition); 994 } 995 996 private Statement createReturningIfStatement(boolean result, Expression condition) { 997 IfStatement firstIf= fAst.newIfStatement(); 998 firstIf.setExpression(condition); 999 1000 ReturnStatement returner= fAst.newReturnStatement(); 1001 returner.setExpression(fAst.newBooleanLiteral(result)); 1002 firstIf.setThenStatement(returner); 1003 return firstIf; 1004 } 1005 1006 private void createMethodComment(MethodDeclaration newDeclaration, IMethodBinding copyFrom) throws CoreException { 1007 if (fSettings.createComments) { 1008 String string= CodeGeneration.getMethodComment(fRewrite.getCu(), fType.getQualifiedName(), newDeclaration, copyFrom, StubUtility.getLineDelimiterUsed(fRewrite.getCu())); 1009 if (string != null) { 1010 Javadoc javadoc= (Javadoc) fRewrite.getASTRewrite().createStringPlaceholder(string, ASTNode.JAVADOC); 1011 newDeclaration.setJavadoc(javadoc); 1012 } 1013 } 1014 if (fSettings.overrideAnnotation && JavaModelUtil.is50OrHigher(fUnit.getJavaElement().getJavaProject())) 1015 StubUtility2.addOverrideAnnotation(fRewrite.getASTRewrite(), newDeclaration, copyFrom); 1016 } 1017 1018 private boolean needsNoSuperCall(ITypeBinding typeBinding, String name, ITypeBinding[] parameters) { 1019 Assert.isNotNull(typeBinding); 1020 IMethodBinding binding= Bindings.findMethodInHierarchy(typeBinding.getSuperclass(), name, parameters); 1021 if (binding != null) { 1022 ITypeBinding declaring= binding.getDeclaringClass(); 1023 return declaring.getQualifiedName().equals(JAVA_LANG_OBJECT); 1024 } 1025 return true; 1026 } 1027 1028 1029 private Expression getThisAccessForEquals(String name) { 1030 return getThisAccess(name, false); 1031 } 1032 1033 private Expression getThisAccessForHashCode(String name) { 1034 return getThisAccess(name, true); 1035 } 1036 1037 private Expression getThisAccess(String name, boolean forHashCode) { 1038 if (fSettings.useKeywordThis || needsThisQualification(name, forHashCode)) { 1039 FieldAccess fa= fAst.newFieldAccess(); 1040 fa.setExpression(fAst.newThisExpression()); 1041 fa.setName(fAst.newSimpleName(name)); 1042 return fa; 1043 } 1044 return fAst.newSimpleName(name); 1045 } 1046 1047 private Expression getOtherAccess(String name) { 1048 return fAst.newQualifiedName(fAst.newSimpleName(VARIABLE_NAME_EQUALS_CASTED), fAst.newSimpleName(name)); 1049 } 1050 1051 private boolean isPrimitiveType(ITypeBinding binding, PrimitiveType.Code code) { 1052 return (binding.getName().equals(code.toString())); 1053 } 1054 1055 private boolean isPrimitiveType(ITypeBinding type, PrimitiveType.Code[] codes) { 1056 for (int i= 0; i < codes.length; i++) { 1057 PrimitiveType.Code code= codes[i]; 1058 if (isPrimitiveType(type, code)) 1059 return true; 1060 } 1061 return false; 1062 } 1063 1064 private Name getQualifiedName(String name) { 1065 String importedType= fRewrite.getImportRewrite().addImport(name); 1066 return ASTNodeFactory.newName(fAst, importedType); 1067 } 1068 1069 private ReturnStatement getReturnFalse() { 1070 ReturnStatement falseReturn= fAst.newReturnStatement(); 1071 falseReturn.setExpression(fAst.newBooleanLiteral(false)); 1072 return falseReturn; 1073 } 1074 1075 private Expression parenthesize(Expression expression) { 1076 ParenthesizedExpression pe= fAst.newParenthesizedExpression(); 1077 pe.setExpression(expression); 1078 return pe; 1079 } 1080 1081 private Expression createFloatInvocation(Expression access) { 1082 return createMethodInvocation(access, "java.lang.Float", "floatToIntBits"); } 1084 1085 private Expression createDoubleInvocation(Expression access) { 1086 return createMethodInvocation(access, "java.lang.Double", "doubleToLongBits"); } 1088 1089 private Expression createMethodInvocation(Expression access, String qualifiedClassName, String methodName) { 1090 MethodInvocation invoc= fAst.newMethodInvocation(); 1091 invoc.setExpression(getQualifiedName(qualifiedClassName)); 1092 invoc.setName(fAst.newSimpleName(methodName)); 1093 invoc.arguments().add(access); 1094 return invoc; 1095 } 1096 1097 private boolean needsThisQualification(String name, boolean isHashCode) { 1098 if (isHashCode) 1099 return ( (fDoubleCount > 0 && name.equals(VARIABLE_NAME_DOUBLE_TEMPORARY)) || (name.equals(VARIABLE_NAME_PRIME)) || (name 1100 .equals(VARIABLE_NAME_RESULT))); 1101 return ( (name.equals(VARIABLE_NAME_EQUALS_CASTED)) || (name.equals(VARIABLE_NAME_EQUALS_PARAM))); 1102 } 1103 1104} 1105 | Popular Tags |