]> git.uio.no Git - ifi-stolz-refaktor.git/blame - case-study/jdt-after/core extension/org/eclipse/jdt/internal/corext/codemanipulation/GenerateHashCodeEqualsOperation.java
Case Study: adding data and statistics
[ifi-stolz-refaktor.git] / case-study / jdt-after / core extension / org / eclipse / jdt / internal / corext / codemanipulation / GenerateHashCodeEqualsOperation.java
CommitLineData
1b2798f6
EK
1/*******************************************************************************
2 * Copyright (c) 2000, 2011 IBM Corporation and others.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the Eclipse Public License v1.0
5 * which accompanies this distribution, and is available at
6 * http://www.eclipse.org/legal/epl-v10.html
7 *
8 * Contributors:
9 * IBM Corporation - initial API and implementation
10 *******************************************************************************/
11package org.eclipse.jdt.internal.corext.codemanipulation;
12
13import java.util.ArrayList;
14import java.util.Iterator;
15import java.util.List;
16
17import org.eclipse.core.runtime.Assert;
18import org.eclipse.core.runtime.CoreException;
19import org.eclipse.core.runtime.IProgressMonitor;
20import org.eclipse.core.runtime.NullProgressMonitor;
21import org.eclipse.core.runtime.jobs.ISchedulingRule;
22
23import org.eclipse.core.resources.IWorkspaceRunnable;
24import org.eclipse.core.resources.ResourcesPlugin;
25
26import org.eclipse.text.edits.TextEdit;
27
28import org.eclipse.jdt.core.ICompilationUnit;
29import org.eclipse.jdt.core.IJavaElement;
30import org.eclipse.jdt.core.IJavaProject;
31import org.eclipse.jdt.core.dom.AST;
32import org.eclipse.jdt.core.dom.ASTNode;
33import org.eclipse.jdt.core.dom.ArrayAccess;
34import org.eclipse.jdt.core.dom.Assignment;
35import org.eclipse.jdt.core.dom.Block;
36import org.eclipse.jdt.core.dom.BodyDeclaration;
37import org.eclipse.jdt.core.dom.CastExpression;
38import org.eclipse.jdt.core.dom.CompilationUnit;
39import org.eclipse.jdt.core.dom.ConditionalExpression;
40import org.eclipse.jdt.core.dom.Expression;
41import org.eclipse.jdt.core.dom.FieldAccess;
42import org.eclipse.jdt.core.dom.ForStatement;
43import org.eclipse.jdt.core.dom.IMethodBinding;
44import org.eclipse.jdt.core.dom.ITypeBinding;
45import org.eclipse.jdt.core.dom.IVariableBinding;
46import org.eclipse.jdt.core.dom.IfStatement;
47import org.eclipse.jdt.core.dom.InfixExpression;
48import org.eclipse.jdt.core.dom.InfixExpression.Operator;
49import org.eclipse.jdt.core.dom.Javadoc;
50import org.eclipse.jdt.core.dom.MethodDeclaration;
51import org.eclipse.jdt.core.dom.MethodInvocation;
52import org.eclipse.jdt.core.dom.Modifier;
53import org.eclipse.jdt.core.dom.Modifier.ModifierKeyword;
54import org.eclipse.jdt.core.dom.Name;
55import org.eclipse.jdt.core.dom.ParenthesizedExpression;
56import org.eclipse.jdt.core.dom.PostfixExpression;
57import org.eclipse.jdt.core.dom.PrefixExpression;
58import org.eclipse.jdt.core.dom.PrimitiveType;
59import org.eclipse.jdt.core.dom.ReturnStatement;
60import org.eclipse.jdt.core.dom.SingleVariableDeclaration;
61import org.eclipse.jdt.core.dom.Statement;
62import org.eclipse.jdt.core.dom.SuperMethodInvocation;
63import org.eclipse.jdt.core.dom.TagElement;
64import org.eclipse.jdt.core.dom.TextElement;
65import org.eclipse.jdt.core.dom.ThisExpression;
66import org.eclipse.jdt.core.dom.VariableDeclarationExpression;
67import org.eclipse.jdt.core.dom.VariableDeclarationFragment;
68import org.eclipse.jdt.core.dom.VariableDeclarationStatement;
69import org.eclipse.jdt.core.dom.rewrite.ImportRewrite.ImportRewriteContext;
70import org.eclipse.jdt.core.dom.rewrite.ListRewrite;
71
72import org.eclipse.jdt.internal.corext.dom.ASTNodeFactory;
73import org.eclipse.jdt.internal.corext.dom.Bindings;
74import org.eclipse.jdt.internal.corext.refactoring.structure.CompilationUnitRewrite;
75import org.eclipse.jdt.internal.corext.util.JavaModelUtil;
76
77/**
78 * <p>
79 * Workspace runnable to add implementations for
80 * <code>{@link java.lang.Object#equals(java.lang.Object)}</code> and
81 * <code>{@link java.lang.Object#hashCode()}</code>.
82 * </p>
83 *
84 * <p>
85 * This implementation creates a hashCode() and an equals() method intended to
86 * be used in value types: The implementation assumes that two objects are equal
87 * (and provide the same hashCode) if all values of all fields are equal.
88 * </p>
89 *
90 * <p>
91 * About the implementation:
92 * <ul>
93 * <li>To deal with reference types of fields and with supertypes, the
94 * implementation calls hashCode() and equals() on reference types of fields and
95 * on the superclass (except if the superclass is Object). It is an error if
96 * these types do not provide an equals() and hashCode() implementation and the
97 * comparison or hash code creation <strong>will fail</strong> if these methods
98 * are not correctly implemented.</li>
99 * <li>The implementation for primitive typed fields is the same as in the JDK
100 * implementations in the wrapper types (and the java.util.Arrays class).</li>
101 * <li>The equals() implementation uses equality of the declaring class instead
102 * of an instanceof check.</li>
103 * <li>A test for null in equals() is only implemented in direct subclasses of
104 * Object. This is only sufficient if every subimplementation calls
105 * super.equals() before any argument checks.</li>
106 * <li>Both equals() and hashCode() use methods from java.util.Arrays to test
107 * for equality and to generate a hash code for arrays. Note that this has an
108 * implication for Double and Float values (consider values -0.0 and 0.0 as well
109 * as border values like NaN and their equality in bit patterns) - however, the
110 * implementation is consistent with the wrapper types java.lang.Float and
111 * java.lang.Double.</li>
112 * </ul>
113 * </p>
114 *
115 * @since 3.2
116 */
117public final class GenerateHashCodeEqualsOperation implements IWorkspaceRunnable {
118
119 private interface IHashCodeAccessProvider {
120
121 public Expression getThisAccess(String name);
122 }
123
124 private static final String JAVA_UTIL_ARRAYS= "java.util.Arrays"; //$NON-NLS-1$
125
126 private static final String BOOLEAN_TRUE_CONSTANT= "1231"; //$NON-NLS-1$
127
128 private static final String BOOLEAN_FALSE_CONSTANT= "1237"; //$NON-NLS-1$
129
130 public static final String JAVA_LANG_OBJECT= "java.lang.Object"; //$NON-NLS-1$
131
132 public static final String METHODNAME_GETCLASS= "getClass"; //$NON-NLS-1$
133
134 public static final String METHODNAME_EQUALS= "equals"; //$NON-NLS-1$
135
136 private static final String METHODNAME_DEEP_EQUALS= "deepEquals"; //$NON-NLS-1$
137
138 public static final String METHODNAME_HASH_CODE= "hashCode"; //$NON-NLS-1$
139
140 public static final String METHODNAME_OUTER_TYPE= "getOuterType"; //$NON-NLS-1$
141
142 private static final String PRIME_NUMBER= "31"; //$NON-NLS-1$
143
144 private static final String INITIAL_HASHCODE_VALUE= "1"; //$NON-NLS-1$
145
146 private static final String VARIABLE_NAME_DOUBLE_TEMPORARY= "temp"; //$NON-NLS-1$
147
148 private static final String VARIABLE_NAME_PRIME= "prime"; //$NON-NLS-1$
149
150 private static final String VARIABLE_NAME_RESULT= "result"; //$NON-NLS-1$
151
152 public static final String VARIABLE_NAME_EQUALS_PARAM= "obj"; //$NON-NLS-1$
153
154 private static final String VARIABLE_NAME_HASHCODE_PARAM= "array"; //$NON-NLS-1$
155
156 public static final String VARIABLE_NAME_EQUALS_CASTED= "other"; //$NON-NLS-1$
157
158 private static final String VARIABLE_NAME_INDEX= "index"; //$NON-NLS-1$
159
160 /** Should the resulting edit be applied? */
161 public final boolean fApply;
162
163 /** The resulting text edit */
164 public TextEdit fEdit= null;
165
166 /** The insertion point, or <code>null</code> */
167 public final IJavaElement fInsert;
168
169 /** The variable binding keys to implement */
170 public final IVariableBinding[] fFields;
171
172 /** Should the regeneration of the methods be enforced? */
173 public final boolean fForce;
174
175 /** Should the compilation unit content be saved? */
176 public final boolean fSave;
177
178 /** The code generation settings to use */
179 public final CodeGenerationSettings fSettings;
180
181 /** The type declaration to add the methods to */
182 public final ITypeBinding fType;
183
184 /** The compilation unit ast node */
185 public final CompilationUnit fUnit;
186
187 /** The CURewrite to be used */
188 public final CompilationUnitRewrite fRewrite;
189
190 /** The ast to be used. Convenience accessor field */
191 public final AST fAst;
192
193 /** The number of double-typed fields handled so far */
194 private int fDoubleCount;
195
196 /** The primitive types to generate custom hashCode() methods for */
197 public List<ITypeBinding> fCustomHashCodeTypes= new ArrayList<ITypeBinding>();
198
199 /** <code>true</code> to use 'instanceof' to compare types, <code>false</code> otherwise */
200 public final boolean fUseInstanceOf;
201
202 /** <code>true</code> to use blocks for then */
203 private boolean fUseBlocksForThen;
204
205 /** The import rewrite context, only initialized in {@link #run(IProgressMonitor)}. */
206 public ImportRewriteContext fImportRewriteContext;
207
208 /**
209 * Creates a new add hash code equals operation.
210 *
211 * @param type the type to add the methods to
212 * @param fields the method binding keys to implement
213 * @param unit the compilation unit ast node
214 * @param insert the insertion point, or <code>null</code>
215 * @param settings the code generation settings to use
216 * @param useInstanceof <code>true</code> to use 'instanceof' to compare types, <code>false</code> otherwise
217 * @param force <code>true</code> to force the regeneration of existing methods,
218 * <code>false</code> otherwise
219 * @param apply <code>true</code> if the resulting edit should be applied,
220 * <code>false</code> otherwise
221 * @param save <code>true</code> if the changed compilation unit should be
222 * saved, <code>false</code> otherwise
223 */
224 public GenerateHashCodeEqualsOperation(final ITypeBinding type, final IVariableBinding[] fields, final CompilationUnit unit,
225 final IJavaElement insert, final CodeGenerationSettings settings, final boolean useInstanceof, final boolean force, final boolean apply, final boolean save) {
226 Assert.isNotNull(type);
227 Assert.isNotNull(fields);
228 Assert.isNotNull(unit);
229 Assert.isNotNull(settings);
230 Assert.isTrue(unit.getTypeRoot() instanceof ICompilationUnit);
231
232 fType= type;
233 fInsert= insert;
234 fUnit= unit;
235 fFields= fields;
236 fSettings= settings;
237 fUseInstanceOf= useInstanceof;
238 fSave= save;
239 fApply= apply;
240 fDoubleCount= 0;
241 fRewrite= new CompilationUnitRewrite((ICompilationUnit) fUnit.getTypeRoot(), fUnit);
242 fForce= force;
243 fAst= fRewrite.getAST();
244 fUseBlocksForThen= false;
245 }
246
247 /**
248 * Defines if then statements should use blocks or not.
249 *
250 * @param useBlocksForThen if set, blocks are forced in if-then statements
251 */
252 public void setUseBlocksForThen(boolean useBlocksForThen) {
253 fUseBlocksForThen= useBlocksForThen;
254 }
255
256 /**
257 * Returns the resulting text edit.
258 *
259 * @return the resulting edit
260 */
261 public final TextEdit getResultingEdit() {
262 return fEdit;
263 }
264
265 /**
266 * Returns the scheduling rule for this operation.
267 *
268 * @return the scheduling rule
269 */
270 public final ISchedulingRule getSchedulingRule() {
271 return ResourcesPlugin.getWorkspace().getRoot();
272 }
273
274 /*
275 * @see org.eclipse.core.resources.IWorkspaceRunnable#run(org.eclipse.core.runtime.IProgressMonitor)
276 */
277 public final void run(IProgressMonitor monitor) throws CoreException {
278 if (monitor == null)
279 monitor= new NullProgressMonitor();
280 try {
281 fRewrite.generated_1836712646092749790(this, monitor);
282 } finally {
283 monitor.done();
284 }
285 }
286
287 public boolean isMemberType() {
288 return fType.isMember() && !Modifier.isStatic(fType.getModifiers());
289 }
290
291 public BodyDeclaration findMethodToReplace(final List<BodyDeclaration> list, String name, ITypeBinding[] paramTypes) {
292 for (final Iterator<BodyDeclaration> iterator= list.iterator(); iterator.hasNext();) {
293 final BodyDeclaration bodyDecl= iterator.next();
294 if (bodyDecl instanceof MethodDeclaration) {
295 final MethodDeclaration method= (MethodDeclaration) bodyDecl;
296 final IMethodBinding binding= method.resolveBinding();
297 if (binding != null && binding.getName().equals(name)) {
298 if (Bindings.equals(binding.getParameterTypes(), paramTypes)) {
299 return method;
300 }
301 }
302 }
303 }
304 return null;
305 }
306
307 public void addHelper(ListRewrite rewriter, ASTNode insertion, MethodDeclaration stub) {
308 if (insertion != null)
309 rewriter.insertBefore(stub, insertion, null);
310 else
311 rewriter.insertFirst(stub, null);
312 }
313
314 public void addMethod(ListRewrite rewriter, ASTNode insertion, MethodDeclaration stub, BodyDeclaration replace) {
315 if (replace != null) {
316 rewriter.replace(replace, stub, null);
317 } else {
318 if (insertion != null)
319 rewriter.insertBefore(stub, insertion, null);
320 else
321 rewriter.insertLast(stub, null);
322 }
323 }
324
325 // ******************* HASHCODE *******************
326
327 public MethodDeclaration createHashCodeMethod() throws CoreException {
328
329 MethodDeclaration hashCodeMethod= fAst.newMethodDeclaration();
330 hashCodeMethod.modifiers().addAll(ASTNodeFactory.newModifiers(fAst, Modifier.PUBLIC));
331 hashCodeMethod.setName(fAst.newSimpleName(METHODNAME_HASH_CODE));
332 hashCodeMethod.setConstructor(false);
333 hashCodeMethod.setReturnType2(fAst.newPrimitiveType(PrimitiveType.INT));
334
335 Block body= fAst.newBlock();
336 hashCodeMethod.setBody(body);
337
338 // PRIME NUMBER
339 VariableDeclarationFragment frag= fAst.newVariableDeclarationFragment();
340 frag.setName(fAst.newSimpleName(VARIABLE_NAME_PRIME));
341 frag.setInitializer(fAst.newNumberLiteral(PRIME_NUMBER));
342
343 VariableDeclarationStatement primeNumberDeclaration= fAst.newVariableDeclarationStatement(frag);
344 primeNumberDeclaration.modifiers().add(fAst.newModifier(ModifierKeyword.FINAL_KEYWORD));
345 primeNumberDeclaration.setType(fAst.newPrimitiveType(PrimitiveType.INT));
346 body.statements().add(primeNumberDeclaration);
347
348 // RESULT
349 VariableDeclarationFragment fragment= fAst.newVariableDeclarationFragment();
350 fragment.setName(fAst.newSimpleName(VARIABLE_NAME_RESULT));
351
352 VariableDeclarationStatement resultDeclaration= fAst.newVariableDeclarationStatement(fragment);
353 resultDeclaration.setType(fAst.newPrimitiveType(PrimitiveType.INT));
354 body.statements().add(resultDeclaration);
355
356 if (needsNoSuperCall(fType, METHODNAME_HASH_CODE, new ITypeBinding[0])) {
357 fragment.setInitializer(fAst.newNumberLiteral(INITIAL_HASHCODE_VALUE));
358 } else {
359 SuperMethodInvocation invoc= fAst.newSuperMethodInvocation();
360 invoc.setName(fAst.newSimpleName(METHODNAME_HASH_CODE));
361 fragment.setInitializer(invoc);
362 }
363
364 if (isMemberType()) {
365 body.statements().add(createAddOuterHashCode());
366 }
367
368 for (int i= 0; i < fFields.length; i++) {
369 if (fFields[i].getType().isPrimitive()) {
370 Statement[] sts= createAddSimpleHashCode(fFields[i].getType(), new IHashCodeAccessProvider() {
371
372 public Expression getThisAccess(String name) {
373 return getThisAccessForHashCode(name);
374 }
375
376 }, fFields[i].getName(), false);
377 for (int j= 0; j < sts.length; j++) {
378 body.statements().add(sts[j]);
379 }
380 } else if (fFields[i].getType().isArray())
381 body.statements().add(createAddArrayHashCode(fFields[i]));
382 else
383 body.statements().add(createAddQualifiedHashCode(fFields[i]));
384 }
385
386 // the last return:
387 ReturnStatement endReturn= fAst.newReturnStatement();
388 endReturn.setExpression(fAst.newSimpleName(VARIABLE_NAME_RESULT));
389 body.statements().add(endReturn);
390
391 // method comment
392 if (fSettings != null) {
393 ITypeBinding object= fAst.resolveWellKnownType(JAVA_LANG_OBJECT);
394 IMethodBinding[] objms= object.getDeclaredMethods();
395 IMethodBinding objectMethod= null;
396 for (int i= 0; i < objms.length; i++) {
397 if (objms[i].getName().equals(METHODNAME_HASH_CODE) && objms[i].getParameterTypes().length == 0)
398 objectMethod= objms[i];
399 }
400 createMethodComment(hashCodeMethod, objectMethod);
401 }
402
403 return hashCodeMethod;
404 }
405
406 private Statement createAddOuterHashCode() {
407 MethodInvocation outer= fAst.newMethodInvocation();
408 outer.setName(fAst.newSimpleName(METHODNAME_OUTER_TYPE));
409 MethodInvocation hashAccess= fAst.newMethodInvocation();
410 hashAccess.setName(fAst.newSimpleName(METHODNAME_HASH_CODE));
411 hashAccess.setExpression(outer);
412 return prepareAssignment(hashAccess);
413 }
414
415 private Statement[] createAddSimpleHashCode(ITypeBinding type, IHashCodeAccessProvider provider, String name, boolean singleTemp) {
416
417 List<Statement> statements= new ArrayList<Statement>();
418
419 if (!type.isPrimitive()) {
420 // (element == null ? 0 : element.hashCode())
421 ConditionalExpression ce= fAst.newConditionalExpression();
422 InfixExpression exp= fAst.newInfixExpression();
423 ArrayAccess access= fAst.newArrayAccess();
424 access.setArray(fAst.newSimpleName(VARIABLE_NAME_HASHCODE_PARAM));
425 access.setIndex(fAst.newSimpleName(VARIABLE_NAME_INDEX));
426 exp.setLeftOperand(access);
427 exp.setOperator(Operator.EQUALS);
428 exp.setRightOperand(fAst.newNullLiteral());
429 ce.setExpression(exp);
430 ce.setThenExpression(fAst.newNumberLiteral("0")); //$NON-NLS-1$
431 MethodInvocation invoc= fAst.newMethodInvocation();
432 access= fAst.newArrayAccess();
433 access.setArray(fAst.newSimpleName(VARIABLE_NAME_HASHCODE_PARAM));
434 access.setIndex(fAst.newSimpleName(VARIABLE_NAME_INDEX));
435 invoc.setExpression(access);
436 invoc.setName(fAst.newSimpleName(METHODNAME_HASH_CODE));
437 ce.setElseExpression(invoc);
438 statements.add(prepareAssignment(parenthesize(ce)));
439 } else if (isPrimitiveType(type, PrimitiveType.BOOLEAN)) {
440 ConditionalExpression ce= fAst.newConditionalExpression();
441 ce.setExpression(provider.getThisAccess(name));
442 // see Boolean.hashCode(boolean)
443 ce.setThenExpression(fAst.newNumberLiteral(BOOLEAN_TRUE_CONSTANT));
444 ce.setElseExpression(fAst.newNumberLiteral(BOOLEAN_FALSE_CONSTANT));
445 statements.add(prepareAssignment(parenthesize(ce)));
446 } else if (isPrimitiveType(type, new PrimitiveType.Code[] { PrimitiveType.CHAR, PrimitiveType.INT, PrimitiveType.SHORT, PrimitiveType.BYTE })) {
447 statements.add(prepareAssignment(provider.getThisAccess(name)));
448 } else if (isPrimitiveType(type, PrimitiveType.FLOAT)) {
449 // Float.floatToIntBits(aFloat)
450 statements.add(prepareAssignment(createFloatInvocation(provider.getThisAccess(name))));
451 } else if (isPrimitiveType(type, PrimitiveType.LONG)) {
452 statements.add(prepareAssignment(createShiftAssignment(provider.getThisAccess(name), provider.getThisAccess(name))));
453 } else if (isPrimitiveType(type, PrimitiveType.DOUBLE)) {
454
455 VariableDeclarationFragment fragment= null;
456 if (singleTemp || fDoubleCount == 0) {
457 fragment= fAst.newVariableDeclarationFragment();
458 fragment.setName(fAst.newSimpleName(VARIABLE_NAME_DOUBLE_TEMPORARY));
459
460 VariableDeclarationStatement st2= fAst.newVariableDeclarationStatement(fragment);
461 st2.setType(fAst.newPrimitiveType(PrimitiveType.LONG));
462 statements.add(st2);
463 }
464 fDoubleCount++;
465
466 // Double.doubleToIntBits(aDouble)
467 Expression comparison= createDoubleInvocation(provider.getThisAccess(name));
468
469 if (singleTemp)
470 fragment.setInitializer(comparison);
471 else {
472 Assignment ass= fAst.newAssignment();
473 ass.setLeftHandSide(fAst.newSimpleName(VARIABLE_NAME_DOUBLE_TEMPORARY));
474 ass.setRightHandSide(comparison);
475 statements.add(fAst.newExpressionStatement(ass));
476 }
477 statements.add(prepareAssignment(createShiftAssignment(fAst.newSimpleName(VARIABLE_NAME_DOUBLE_TEMPORARY), fAst.newSimpleName(VARIABLE_NAME_DOUBLE_TEMPORARY))));
478 }
479
480 return statements.toArray(new Statement[statements.size()]);
481 }
482
483 private Statement createAddArrayHashCode(IVariableBinding binding) {
484 MethodInvocation invoc= fAst.newMethodInvocation();
485 if (JavaModelUtil.is50OrHigher(fRewrite.getCu().getJavaProject())) {
486 invoc.setName(fAst.newSimpleName(METHODNAME_HASH_CODE));
487 invoc.setExpression(getQualifiedName(JAVA_UTIL_ARRAYS));
488 invoc.arguments().add(getThisAccessForHashCode(binding.getName()));
489 } else {
490 invoc.setName(fAst.newSimpleName(METHODNAME_HASH_CODE));
491 final IJavaElement element= fType.getJavaElement();
492 if (element != null && !"".equals(element.getElementName())) //$NON-NLS-1$
493 invoc.setExpression(fAst.newSimpleName(element.getElementName()));
494 invoc.arguments().add(getThisAccessForHashCode(binding.getName()));
495 ITypeBinding type= binding.getType().getElementType();
496 if (!Bindings.isVoidType(type)) {
497 if (!type.isPrimitive() || binding.getType().getDimensions() >= 2)
498 type= fAst.resolveWellKnownType(JAVA_LANG_OBJECT);
499 if (!fCustomHashCodeTypes.contains(type))
500 fCustomHashCodeTypes.add(type);
501 }
502 }
503 return prepareAssignment(invoc);
504 }
505
506 public MethodDeclaration createGetOuterHelper() {
507 String outerTypeName= fType.getDeclaringClass().getTypeDeclaration().getName();
508
509 MethodDeclaration helperMethod= fAst.newMethodDeclaration();
510 helperMethod.modifiers().addAll(ASTNodeFactory.newModifiers(fAst, Modifier.PRIVATE));
511 helperMethod.setName(fAst.newSimpleName(METHODNAME_OUTER_TYPE));
512 helperMethod.setConstructor(false);
513 helperMethod.setReturnType2(fAst.newSimpleType(fAst.newSimpleName(outerTypeName)));
514
515 Block body= fAst.newBlock();
516 helperMethod.setBody(body);
517
518 ThisExpression thisExpression= fAst.newThisExpression();
519 thisExpression.setQualifier(fAst.newSimpleName(outerTypeName));
520
521 ReturnStatement endReturn= fAst.newReturnStatement();
522 endReturn.setExpression(thisExpression);
523 body.statements().add(endReturn);
524
525 return helperMethod;
526 }
527
528
529 public MethodDeclaration createHashCodeHelper(ITypeBinding binding) {
530 Assert.isTrue(!binding.isArray());
531
532 MethodDeclaration hashCodeMethod= fAst.newMethodDeclaration();
533 hashCodeMethod.modifiers().addAll(ASTNodeFactory.newModifiers(fAst, Modifier.PRIVATE | Modifier.STATIC));
534 hashCodeMethod.setName(fAst.newSimpleName(METHODNAME_HASH_CODE));
535 hashCodeMethod.setConstructor(false);
536 hashCodeMethod.setReturnType2(fAst.newPrimitiveType(PrimitiveType.INT));
537
538 // ARGUMENTS
539 List<SingleVariableDeclaration> parameters= hashCodeMethod.parameters();
540 SingleVariableDeclaration hashCodeParam= fAst.newSingleVariableDeclaration();
541 if (!binding.isPrimitive())
542 hashCodeParam.setType(fAst.newArrayType(fAst.newSimpleType(getQualifiedName(JAVA_LANG_OBJECT)), 1));
543 else
544 hashCodeParam.setType(fAst.newArrayType(fAst.newPrimitiveType(PrimitiveType.toCode(binding.getName())), 1));
545 hashCodeParam.setName(fAst.newSimpleName(VARIABLE_NAME_HASHCODE_PARAM));
546 parameters.add(hashCodeParam);
547
548 Block body= fAst.newBlock();
549 hashCodeMethod.setBody(body);
550
551 // PRIME NUMBER
552 VariableDeclarationFragment frag= fAst.newVariableDeclarationFragment();
553 frag.setName(fAst.newSimpleName(VARIABLE_NAME_PRIME));
554 frag.setInitializer(fAst.newNumberLiteral(PRIME_NUMBER));
555
556 VariableDeclarationStatement primeNumberDeclaration= fAst.newVariableDeclarationStatement(frag);
557 primeNumberDeclaration.setType(fAst.newPrimitiveType(PrimitiveType.INT));
558 body.statements().add(primeNumberDeclaration);
559
560 // IF STATEMENT
561 IfStatement ifStatement= fAst.newIfStatement();
562 final InfixExpression newInfixExpression= fAst.newInfixExpression();
563 newInfixExpression.setLeftOperand(fAst.newSimpleName(VARIABLE_NAME_HASHCODE_PARAM));
564 newInfixExpression.setRightOperand(fAst.newNullLiteral());
565 newInfixExpression.setOperator(Operator.EQUALS);
566 ifStatement.setExpression(newInfixExpression);
567 final ReturnStatement returnStatement= fAst.newReturnStatement();
568 returnStatement.setExpression(fAst.newNumberLiteral("0")); //$NON-NLS-1$
569 ifStatement.setThenStatement(getThenStatement(returnStatement));
570 body.statements().add(ifStatement);
571
572 // RESULT
573 VariableDeclarationFragment resultFragment= fAst.newVariableDeclarationFragment();
574 resultFragment.setName(fAst.newSimpleName(VARIABLE_NAME_RESULT));
575 resultFragment.setInitializer(fAst.newNumberLiteral(INITIAL_HASHCODE_VALUE));
576 VariableDeclarationStatement resultDeclaration= fAst.newVariableDeclarationStatement(resultFragment);
577 resultDeclaration.setType(fAst.newPrimitiveType(PrimitiveType.INT));
578 body.statements().add(resultDeclaration);
579
580 // FOR LOOP
581 ForStatement forStatement= fAst.newForStatement();
582
583 VariableDeclarationFragment indexDeclaration= fAst.newVariableDeclarationFragment();
584 indexDeclaration.setName(fAst.newSimpleName(VARIABLE_NAME_INDEX));
585 indexDeclaration.setInitializer(fAst.newNumberLiteral("0")); //$NON-NLS-1$
586 final VariableDeclarationExpression declExpression= fAst.newVariableDeclarationExpression(indexDeclaration);
587 declExpression.setType(fAst.newPrimitiveType(PrimitiveType.INT));
588 forStatement.initializers().add(declExpression);
589 InfixExpression infixExpr= fAst.newInfixExpression();
590 infixExpr.setLeftOperand(fAst.newSimpleName(VARIABLE_NAME_INDEX));
591 FieldAccess access= fAst.newFieldAccess();
592 access.setExpression(fAst.newSimpleName(VARIABLE_NAME_HASHCODE_PARAM));
593 access.setName(fAst.newSimpleName("length")); //$NON-NLS-1$
594 infixExpr.setRightOperand(access);
595 infixExpr.setOperator(Operator.LESS);
596 forStatement.setExpression(infixExpr);
597 PostfixExpression postfixExpr= fAst.newPostfixExpression();
598 postfixExpr.setOperand(fAst.newSimpleName(VARIABLE_NAME_INDEX));
599 postfixExpr.setOperator(org.eclipse.jdt.core.dom.PostfixExpression.Operator.INCREMENT);
600 forStatement.updaters().add(postfixExpr);
601 body.statements().add(forStatement);
602
603 Block forBody= fAst.newBlock();
604 Statement[] statements= createAddSimpleHashCode(binding, new IHashCodeAccessProvider() {
605
606 public Expression getThisAccess(String name) {
607 ArrayAccess a= fAst.newArrayAccess();
608 a.setArray(fAst.newSimpleName(VARIABLE_NAME_HASHCODE_PARAM));
609 a.setIndex(fAst.newSimpleName(name));
610 return a;
611 }
612 }, VARIABLE_NAME_INDEX, true);
613 for (int index= 0; index < statements.length; index++) {
614 forBody.statements().add(statements[index]);
615 }
616 forStatement.setBody(forBody);
617
618 // END RETURN
619 ReturnStatement endReturn= fAst.newReturnStatement();
620 endReturn.setExpression(fAst.newSimpleName(VARIABLE_NAME_RESULT));
621 body.statements().add(endReturn);
622
623 // COMMENT
624 if (fSettings != null && fSettings.createComments) {
625 Javadoc javadoc= fAst.newJavadoc();
626 final TagElement tagComment= fAst.newTagElement();
627 TextElement text= fAst.newTextElement();
628 text.setText(CodeGenerationMessages.GenerateHashCodeEqualsOperation_hash_code_comment);
629 tagComment.fragments().add(text);
630 javadoc.tags().add(tagComment);
631 final TagElement tagParam= fAst.newTagElement();
632 tagParam.setTagName(CodeGenerationMessages.GenerateHashCodeEqualsOperation_tag_param);
633 tagParam.fragments().add(fAst.newSimpleName(VARIABLE_NAME_HASHCODE_PARAM));
634 text= fAst.newTextElement();
635 text.setText(CodeGenerationMessages.GenerateHashCodeEqualsOperation_hash_code_argument);
636 tagParam.fragments().add(text);
637 javadoc.tags().add(tagParam);
638 final TagElement tagReturn= fAst.newTagElement();
639 tagReturn.setTagName(CodeGenerationMessages.GenerateHashCodeEqualsOperation_tag_return);
640 text= fAst.newTextElement();
641 text.setText(CodeGenerationMessages.GenerateHashCodeEqualsOperation_return_comment);
642 tagReturn.fragments().add(text);
643 javadoc.tags().add(tagReturn);
644 hashCodeMethod.setJavadoc(javadoc);
645 }
646 return hashCodeMethod;
647 }
648
649 private Statement createAddQualifiedHashCode(IVariableBinding binding) {
650
651 MethodInvocation invoc= fAst.newMethodInvocation();
652 invoc.setExpression(getThisAccessForHashCode(binding.getName()));
653 invoc.setName(fAst.newSimpleName(METHODNAME_HASH_CODE));
654
655 InfixExpression expr= fAst.newInfixExpression();
656 expr.setOperator(Operator.EQUALS);
657 expr.setLeftOperand(getThisAccessForHashCode(binding.getName()));
658 expr.setRightOperand(fAst.newNullLiteral());
659
660 ConditionalExpression cexpr= fAst.newConditionalExpression();
661 cexpr.setThenExpression(fAst.newNumberLiteral("0")); //$NON-NLS-1$
662 cexpr.setElseExpression(invoc);
663 cexpr.setExpression(parenthesize(expr));
664
665 return prepareAssignment(parenthesize(cexpr));
666 }
667
668 private Expression createShiftAssignment(Expression shift1, Expression shift2) {
669 // (int)(element ^ (element >>> 32));
670 // see implementation in Arrays.hashCode(), Double.hashCode() and
671 // Long.hashCode()
672 CastExpression ce= fAst.newCastExpression();
673 ce.setType(fAst.newPrimitiveType(PrimitiveType.INT));
674
675 InfixExpression unsignedShiftRight= fAst.newInfixExpression();
676 unsignedShiftRight.setLeftOperand(shift1);
677 unsignedShiftRight.setRightOperand(fAst.newNumberLiteral("32")); //$NON-NLS-1$
678 unsignedShiftRight.setOperator(Operator.RIGHT_SHIFT_UNSIGNED);
679
680 InfixExpression xor= fAst.newInfixExpression();
681 xor.setLeftOperand(shift2);
682 xor.setRightOperand(parenthesize(unsignedShiftRight));
683 xor.setOperator(InfixExpression.Operator.XOR);
684
685 ce.setExpression(parenthesize(xor));
686 return ce;
687 }
688
689 private Statement prepareAssignment(Expression rightHand) {
690 // result = PRIME*result + (...)
691 InfixExpression mul= fAst.newInfixExpression();
692 mul.setLeftOperand(fAst.newSimpleName(VARIABLE_NAME_PRIME));
693 mul.setRightOperand(fAst.newSimpleName(VARIABLE_NAME_RESULT));
694 mul.setOperator(Operator.TIMES);
695
696 Assignment ass= fAst.newAssignment();
697 ass.setLeftHandSide(fAst.newSimpleName(VARIABLE_NAME_RESULT));
698
699 InfixExpression plus= fAst.newInfixExpression();
700 plus.setLeftOperand(mul);
701 plus.setOperator(Operator.PLUS);
702 plus.setRightOperand(rightHand);
703
704 ass.setRightHandSide(plus);
705
706 return fAst.newExpressionStatement(ass);
707 }
708
709 // *************** EQUALS ***************
710
711 public MethodDeclaration createEqualsMethod() throws CoreException {
712
713 MethodDeclaration equalsMethodDeclaration= fAst.newMethodDeclaration();
714 equalsMethodDeclaration.modifiers().addAll(ASTNodeFactory.newModifiers(fAst, Modifier.PUBLIC));
715 equalsMethodDeclaration.setName(fAst.newSimpleName(METHODNAME_EQUALS));
716 equalsMethodDeclaration.setConstructor(false);
717 equalsMethodDeclaration.setReturnType2(fAst.newPrimitiveType(PrimitiveType.BOOLEAN));
718
719 List<SingleVariableDeclaration> parameters= equalsMethodDeclaration.parameters();
720 return fRewrite.generated_1018980582736112681(this, equalsMethodDeclaration, parameters);
721 }
722
723 public Statement createOuterComparison() {
724 MethodInvocation outer1= fAst.newMethodInvocation();
725 outer1.setName(fAst.newSimpleName(METHODNAME_OUTER_TYPE));
726
727 MethodInvocation outer2= fAst.newMethodInvocation();
728 outer2.setName(fAst.newSimpleName(METHODNAME_OUTER_TYPE));
729 outer2.setExpression(fAst.newSimpleName(VARIABLE_NAME_EQUALS_CASTED));
730
731 MethodInvocation outerEql= fAst.newMethodInvocation();
732 outerEql.setName(fAst.newSimpleName(METHODNAME_EQUALS));
733 outerEql.setExpression(outer1);
734 outerEql.arguments().add(outer2);
735
736 PrefixExpression not= fAst.newPrefixExpression();
737 not.setOperand(outerEql);
738 not.setOperator(PrefixExpression.Operator.NOT);
739
740 IfStatement notEqNull= fAst.newIfStatement();
741 notEqNull.setExpression(not);
742 notEqNull.setThenStatement(getThenStatement(getReturnFalse()));
743 return notEqNull;
744 }
745
746 public Statement createSimpleComparison(IVariableBinding binding) {
747 if (isPrimitiveType(binding.getType(), PrimitiveType.FLOAT)) {
748 return createReturningIfStatement(createFloatInvocation(getThisAccessForEquals(binding.getName())), createFloatInvocation(getOtherAccess(binding
749 .getName())), Operator.NOT_EQUALS, false);
750 } else if (isPrimitiveType(binding.getType(), PrimitiveType.DOUBLE)) {
751 return createReturningIfStatement(createDoubleInvocation(getThisAccessForEquals(binding.getName())), createDoubleInvocation(getOtherAccess(binding
752 .getName())), Operator.NOT_EQUALS, false);
753 } else
754 return createReturningIfStatement(getThisAccessForEquals(binding.getName()), getOtherAccess(binding.getName()), Operator.NOT_EQUALS, false);
755 }
756
757 public Statement createArrayComparison(String name) {
758 MethodInvocation invoc= fAst.newMethodInvocation();
759 invoc.setName(fAst.newSimpleName(METHODNAME_EQUALS));
760 invoc.setExpression(getQualifiedName(JAVA_UTIL_ARRAYS));
761 invoc.arguments().add(getThisAccessForEquals(name));
762 invoc.arguments().add(getOtherAccess(name));
763
764 PrefixExpression pe= fAst.newPrefixExpression();
765 pe.setOperator(PrefixExpression.Operator.NOT);
766 pe.setOperand(invoc);
767
768 IfStatement ifSt= fAst.newIfStatement();
769 ifSt.setExpression(pe);
770 ifSt.setThenStatement(getThenStatement(getReturnFalse()));
771
772 return ifSt;
773 }
774
775 public Statement createMultiArrayComparison(String name) {
776 MethodInvocation invoc= fAst.newMethodInvocation();
777 invoc.setName(fAst.newSimpleName(METHODNAME_DEEP_EQUALS));
778 invoc.setExpression(getQualifiedName(JAVA_UTIL_ARRAYS));
779 invoc.arguments().add(getThisAccessForEquals(name));
780 invoc.arguments().add(getOtherAccess(name));
781
782 PrefixExpression pe= fAst.newPrefixExpression();
783 pe.setOperator(PrefixExpression.Operator.NOT);
784 pe.setOperand(invoc);
785
786 IfStatement ifSt= fAst.newIfStatement();
787 ifSt.setExpression(pe);
788 ifSt.setThenStatement(getThenStatement(getReturnFalse()));
789
790 return ifSt;
791 }
792
793 /**
794 * Creates a comparison of reference types.
795 *
796 * <pre>
797 * if (this.a == null) {
798 * if (other.a != null)
799 * return false;
800 * } else {
801 * if (!this.a.equals(other.a))
802 * return false;
803 * }
804 * </pre>
805 * @param name the field name
806 * @return the comparison statement
807 */
808 public Statement createQualifiedComparison(String name) {
809 InfixExpression newCondition= fAst.newInfixExpression();
810 newCondition.setOperator(Operator.EQUALS);
811 newCondition.setLeftOperand(getThisAccessForEquals(name));
812 newCondition.setRightOperand(fAst.newNullLiteral());
813
814 // THEN
815 InfixExpression notEqNull= fAst.newInfixExpression();
816 notEqNull.setOperator(Operator.NOT_EQUALS);
817 notEqNull.setLeftOperand(getOtherAccess(name));
818 notEqNull.setRightOperand(fAst.newNullLiteral());
819
820 IfStatement thenPart= fAst.newIfStatement();
821 thenPart.setExpression(notEqNull);
822 thenPart.setThenStatement(getThenStatement(getReturnFalse()));
823
824 Block thenPart2= fAst.newBlock();
825 thenPart2.statements().add(thenPart);
826
827 // ELSE
828 MethodInvocation invoc= fAst.newMethodInvocation();
829 invoc.setName(fAst.newSimpleName(METHODNAME_EQUALS));
830 invoc.setExpression(getThisAccessForEquals(name));
831 invoc.arguments().add(getOtherAccess(name));
832
833 PrefixExpression pe= fAst.newPrefixExpression();
834 pe.setOperator(PrefixExpression.Operator.NOT);
835 pe.setOperand(invoc);
836
837 IfStatement elsePart= fAst.newIfStatement();
838 elsePart.setExpression(pe);
839 elsePart.setThenStatement(getThenStatement(getReturnFalse()));
840
841 // ALL
842 IfStatement isNull= fAst.newIfStatement();
843 isNull.setExpression(newCondition);
844 isNull.setThenStatement(thenPart2);
845 isNull.setElseStatement(elsePart);
846
847 return isNull;
848 }
849
850 // ************************ HELPERS **************************
851
852 public Statement createReturningIfStatement(Expression left, Expression right, Operator operator, boolean whatToReturn) {
853 InfixExpression newCondition= fAst.newInfixExpression();
854 newCondition.setOperator(operator);
855 newCondition.setLeftOperand(left);
856 newCondition.setRightOperand(right);
857 return createReturningIfStatement(whatToReturn, newCondition);
858 }
859
860 public Statement createReturningIfStatement(boolean result, Expression condition) {
861 IfStatement firstIf= fAst.newIfStatement();
862 firstIf.setExpression(condition);
863
864 ReturnStatement returner= fAst.newReturnStatement();
865 returner.setExpression(fAst.newBooleanLiteral(result));
866 firstIf.setThenStatement(getThenStatement(returner));
867 return firstIf;
868 }
869
870 public void createMethodComment(MethodDeclaration newDeclaration, IMethodBinding copyFrom) throws CoreException {
871 if (fSettings.createComments) {
872 fRewrite.generated_31859225078671094(this, newDeclaration, copyFrom);
873 }
874 IJavaProject project= fUnit.getJavaElement().getJavaProject();
875 if (fSettings.overrideAnnotation && JavaModelUtil.is50OrHigher(project))
876 StubUtility2.addOverrideAnnotation(project, fRewrite.getASTRewrite(), newDeclaration, copyFrom);
877 }
878
879 public boolean needsNoSuperCall(ITypeBinding typeBinding, String name, ITypeBinding[] parameters) {
880 Assert.isNotNull(typeBinding);
881 IMethodBinding binding= Bindings.findMethodInHierarchy(typeBinding.getSuperclass(), name, parameters);
882 if (binding != null && !Modifier.isAbstract(binding.getModifiers())) {
883 ITypeBinding declaring= binding.getDeclaringClass();
884 return declaring.getQualifiedName().equals(JAVA_LANG_OBJECT);
885 }
886 return true;
887 }
888
889
890 private Expression getThisAccessForEquals(String name) {
891 return getThisAccess(name, false);
892 }
893
894 private Expression getThisAccessForHashCode(String name) {
895 return getThisAccess(name, true);
896 }
897
898 private Expression getThisAccess(String name, boolean forHashCode) {
899 return fSettings.generated_4209894319447273040(this, name, forHashCode);
900 }
901
902 private Expression getOtherAccess(String name) {
903 return fAst.newQualifiedName(fAst.newSimpleName(VARIABLE_NAME_EQUALS_CASTED), fAst.newSimpleName(name));
904 }
905
906 private boolean isPrimitiveType(ITypeBinding binding, PrimitiveType.Code code) {
907 return (binding.getName().equals(code.toString()));
908 }
909
910 private boolean isPrimitiveType(ITypeBinding type, PrimitiveType.Code[] codes) {
911 for (int i= 0; i < codes.length; i++) {
912 PrimitiveType.Code code= codes[i];
913 if (isPrimitiveType(type, code))
914 return true;
915 }
916 return false;
917 }
918
919 private Name getQualifiedName(String name) {
920 String importedType= fRewrite.getImportRewrite().addImport(name, fImportRewriteContext);
921 return ASTNodeFactory.newName(fAst, importedType);
922 }
923
924 public ReturnStatement getReturnFalse() {
925 ReturnStatement falseReturn= fAst.newReturnStatement();
926 falseReturn.setExpression(fAst.newBooleanLiteral(false));
927 return falseReturn;
928 }
929
930 public Statement getThenStatement(Statement statement) {
931 if (fUseBlocksForThen && !(statement instanceof Block)) {
932 Block block= fAst.newBlock();
933 block.statements().add(statement);
934 return block;
935 }
936 return statement;
937 }
938
939
940 private Expression parenthesize(Expression expression) {
941 ParenthesizedExpression pe= fAst.newParenthesizedExpression();
942 pe.setExpression(expression);
943 return pe;
944 }
945
946 private Expression createFloatInvocation(Expression access) {
947 return createMethodInvocation(access, "java.lang.Float", "floatToIntBits"); //$NON-NLS-1$ //$NON-NLS-2$
948 }
949
950 private Expression createDoubleInvocation(Expression access) {
951 return createMethodInvocation(access, "java.lang.Double", "doubleToLongBits"); //$NON-NLS-1$ //$NON-NLS-2$
952 }
953
954 private Expression createMethodInvocation(Expression access, String qualifiedClassName, String methodName) {
955 MethodInvocation invoc= fAst.newMethodInvocation();
956 invoc.setExpression(getQualifiedName(qualifiedClassName));
957 invoc.setName(fAst.newSimpleName(methodName));
958 invoc.arguments().add(access);
959 return invoc;
960 }
961
962 boolean needsThisQualification(String name, boolean isHashCode) {
963 if (isHashCode)
964 return ( (fDoubleCount > 0 && name.equals(VARIABLE_NAME_DOUBLE_TEMPORARY)) || (name.equals(VARIABLE_NAME_PRIME)) || (name
965 .equals(VARIABLE_NAME_RESULT)));
966 return ( (name.equals(VARIABLE_NAME_EQUALS_CASTED)) || (name.equals(VARIABLE_NAME_EQUALS_PARAM)));
967 }
968
969}