1 11 package org.eclipse.jdt.ui.actions; 12 13 import java.lang.reflect.InvocationTargetException ; 14 import java.util.ArrayList ; 15 import java.util.Arrays ; 16 import java.util.List ; 17 18 import org.eclipse.core.runtime.CoreException; 19 20 import org.eclipse.swt.widgets.Shell; 21 22 import org.eclipse.jface.dialogs.Dialog; 23 import org.eclipse.jface.dialogs.MessageDialog; 24 import org.eclipse.jface.operation.IRunnableContext; 25 import org.eclipse.jface.viewers.IStructuredSelection; 26 import org.eclipse.jface.window.Window; 27 28 import org.eclipse.jface.text.IRewriteTarget; 29 import org.eclipse.jface.text.ITextSelection; 30 31 import org.eclipse.ui.IEditorPart; 32 import org.eclipse.ui.IWorkbenchSite; 33 import org.eclipse.ui.PlatformUI; 34 35 import org.eclipse.ltk.core.refactoring.RefactoringStatus; 36 import org.eclipse.ltk.core.refactoring.RefactoringStatusContext; 37 import org.eclipse.ltk.ui.refactoring.RefactoringUI; 38 39 import org.eclipse.jdt.core.IClassFile; 40 import org.eclipse.jdt.core.ICompilationUnit; 41 import org.eclipse.jdt.core.IJavaElement; 42 import org.eclipse.jdt.core.IMember; 43 import org.eclipse.jdt.core.IOpenable; 44 import org.eclipse.jdt.core.ISourceReference; 45 import org.eclipse.jdt.core.IType; 46 import org.eclipse.jdt.core.JavaModelException; 47 import org.eclipse.jdt.core.dom.AST; 48 import org.eclipse.jdt.core.dom.AbstractTypeDeclaration; 49 import org.eclipse.jdt.core.dom.CompilationUnit; 50 import org.eclipse.jdt.core.dom.IMethodBinding; 51 import org.eclipse.jdt.core.dom.ITypeBinding; 52 import org.eclipse.jdt.core.dom.IVariableBinding; 53 import org.eclipse.jdt.core.dom.Modifier; 54 55 import org.eclipse.jdt.internal.corext.codemanipulation.CodeGenerationSettings; 56 import org.eclipse.jdt.internal.corext.codemanipulation.GenerateHashCodeEqualsOperation; 57 import org.eclipse.jdt.internal.corext.dom.ASTNodes; 58 import org.eclipse.jdt.internal.corext.dom.NodeFinder; 59 import org.eclipse.jdt.internal.corext.refactoring.base.JavaStatusContext; 60 import org.eclipse.jdt.internal.corext.refactoring.util.RefactoringASTParser; 61 import org.eclipse.jdt.internal.corext.util.JavaModelUtil; 62 import org.eclipse.jdt.internal.corext.util.Messages; 63 64 import org.eclipse.jdt.ui.JavaUI; 65 66 import org.eclipse.jdt.internal.ui.IJavaHelpContextIds; 67 import org.eclipse.jdt.internal.ui.JavaPlugin; 68 import org.eclipse.jdt.internal.ui.actions.ActionMessages; 69 import org.eclipse.jdt.internal.ui.actions.ActionUtil; 70 import org.eclipse.jdt.internal.ui.actions.SelectionConverter; 71 import org.eclipse.jdt.internal.ui.actions.WorkbenchRunnableAdapter; 72 import org.eclipse.jdt.internal.ui.dialogs.GenerateHashCodeEqualsDialog; 73 import org.eclipse.jdt.internal.ui.javaeditor.CompilationUnitEditor; 74 import org.eclipse.jdt.internal.ui.preferences.JavaPreferencesSettings; 75 import org.eclipse.jdt.internal.ui.util.BusyIndicatorRunnableContext; 76 import org.eclipse.jdt.internal.ui.util.ElementValidator; 77 import org.eclipse.jdt.internal.ui.util.ExceptionHandler; 78 79 97 public final class GenerateHashCodeEqualsAction extends SelectionDispatchAction { 98 99 private static final String METHODNAME_HASH_CODE= "hashCode"; 101 private static final String METHODNAME_EQUALS= "equals"; 103 private CompilationUnitEditor fEditor; 104 105 private CompilationUnit fUnit; 106 107 private ITypeBinding fTypeBinding; 108 109 private IVariableBinding[] fCandidateFields; 110 111 private class HashCodeEqualsInfo { 112 113 public boolean foundHashCode= false; 114 115 public boolean foundEquals= false; 116 117 public boolean foundFinalHashCode= false; 118 119 public boolean foundFinalEquals= false; 120 } 121 122 128 public GenerateHashCodeEqualsAction(final CompilationUnitEditor editor) { 129 this(editor.getEditorSite()); 130 fEditor= editor; 131 setEnabled( (fEditor != null && SelectionConverter.canOperateOn(fEditor))); 132 } 133 134 144 public GenerateHashCodeEqualsAction(final IWorkbenchSite site) { 145 super(site); 146 setText(ActionMessages.GenerateHashCodeEqualsAction_label); 147 setDescription(ActionMessages.GenerateHashCodeEqualsAction_description); 148 setToolTipText(ActionMessages.GenerateHashCodeEqualsAction_tooltip); 149 PlatformUI.getWorkbench().getHelpSystem().setHelp(this, IJavaHelpContextIds.GENERATE_HASHCODE_EQUALS_ACTION); 150 } 151 152 161 private boolean canEnable(final IStructuredSelection selection) throws JavaModelException { 162 if (selection.size() == 1) { 163 final Object element= selection.getFirstElement(); 164 if (element instanceof IType) { 165 final IType type= (IType) element; 166 return type.getCompilationUnit() != null && type.isClass(); 167 } 168 if (element instanceof ICompilationUnit) 169 return true; 170 } 171 return false; 172 } 173 174 182 private IType getSelectedType(final IStructuredSelection selection) throws JavaModelException { 183 if (selection.size() == 1 && selection.getFirstElement() instanceof IType) { 184 final IType type= (IType) selection.getFirstElement(); 185 if (type.getCompilationUnit() != null && type.isClass()) 186 return type; 187 } else if (selection.getFirstElement() instanceof ICompilationUnit) { 188 final ICompilationUnit unit= (ICompilationUnit) selection.getFirstElement(); 189 final IType type= unit.findPrimaryType(); 190 if (type != null && type.isClass()) 191 return type; 192 } 193 return null; 194 } 195 196 199 public void run(IStructuredSelection selection) { 200 try { 201 checkAndRun(getSelectedType(selection)); 202 } catch (CoreException exception) { 203 ExceptionHandler.handle(exception, getShell(), ActionMessages.GenerateHashCodeEqualsAction_error_caption, 204 ActionMessages.GenerateHashCodeEqualsAction_error_cannot_create); 205 } 206 } 207 208 211 public void run(ITextSelection selection) { 212 try { 213 checkAndRun(SelectionConverter.getTypeAtOffset(fEditor)); 214 } catch (CoreException e) { 215 ExceptionHandler.handle(e, getShell(), ActionMessages.GenerateHashCodeEqualsAction_error_caption, 216 ActionMessages.GenerateHashCodeEqualsAction_error_cannot_create); 217 } 218 } 219 220 private void checkAndRun(IType type) throws CoreException { 221 if (type == null) { 222 MessageDialog.openInformation(getShell(), ActionMessages.GenerateHashCodeEqualsAction_error_caption, 223 ActionMessages.GenerateHashCodeEqualsAction_error_not_applicable); 224 notifyResult(false); 225 } 226 if (!ElementValidator.check(type, getShell(), ActionMessages.GenerateHashCodeEqualsAction_error_caption, false) 227 || ! ActionUtil.isEditable(fEditor, getShell(), type)) { 228 notifyResult(false); 229 return; 230 } 231 if (type == null) { 232 MessageDialog.openError(getShell(), ActionMessages.GenerateHashCodeEqualsAction_error_caption, 233 ActionMessages.GenerateHashCodeEqualsAction_error_removed_type); 234 notifyResult(false); 235 return; 236 } 237 if (type.isAnnotation()) { 238 MessageDialog.openInformation(getShell(), ActionMessages.GenerateHashCodeEqualsAction_error_caption, 239 ActionMessages.GenerateHashCodeEqualsAction_annotation_not_applicable); 240 notifyResult(false); 241 return; 242 } 243 if (type.isInterface()) { 244 MessageDialog.openInformation(getShell(), ActionMessages.GenerateHashCodeEqualsAction_error_caption, 245 ActionMessages.GenerateHashCodeEqualsAction_interface_not_applicable); 246 notifyResult(false); 247 return; 248 } 249 if (type.isEnum()) { 250 MessageDialog.openInformation(getShell(), ActionMessages.GenerateHashCodeEqualsAction_error_caption, 251 ActionMessages.GenerateHashCodeEqualsAction_enum_not_applicable); 252 notifyResult(false); 253 return; 254 } 255 if (type.isAnonymous()) { 256 MessageDialog.openError(getShell(), ActionMessages.GenerateHashCodeEqualsAction_error_caption, 257 ActionMessages.GenerateHashCodeEqualsAction_anonymous_type_not_applicable); 258 notifyResult(false); 259 return; 260 } 261 run(getShell(), type); 262 } 263 264 271 private void run(Shell shell, IType type) throws CoreException { 272 273 initialize(type); 274 275 boolean regenerate= false; 276 if (hasHashCodeOrEquals(fTypeBinding)) { 277 regenerate= MessageDialog.openQuestion(getShell(), ActionMessages.GenerateHashCodeEqualsAction_error_caption, Messages.format(ActionMessages.GenerateHashCodeEqualsAction_already_has_hashCode_equals_error, fTypeBinding.getQualifiedName())); 278 if (!regenerate) { 279 notifyResult(false); 280 return; 281 } 282 } 283 284 List allFields= new ArrayList (); 285 List selectedFields= new ArrayList (); 286 for (int i= 0; i < fCandidateFields.length; i++) { 287 if (!Modifier.isStatic(fCandidateFields[i].getModifiers())) { 288 allFields.add(fCandidateFields[i]); 289 if (!Modifier.isTransient(fCandidateFields[i].getModifiers())) 290 selectedFields.add(fCandidateFields[i]); 291 } 292 } 293 294 if (allFields.isEmpty()) { 295 MessageDialog.openInformation(getShell(), ActionMessages.GenerateHashCodeEqualsAction_error_caption, 296 ActionMessages.GenerateHashCodeEqualsAction_no_nonstatic_fields_error); 297 notifyResult(false); 298 return; 299 } 300 301 IVariableBinding[] allFieldBindings= (IVariableBinding[]) allFields.toArray(new IVariableBinding[0]); 302 IVariableBinding[] selectedFieldBindings= (IVariableBinding[]) selectedFields.toArray(new IVariableBinding[0]); 303 304 final GenerateHashCodeEqualsDialog dialog= new GenerateHashCodeEqualsDialog(shell, fEditor, type, allFieldBindings, selectedFieldBindings); 305 final int dialogResult= dialog.open(); 306 if (dialogResult == Window.OK) { 307 308 final Object [] selected= dialog.getResult(); 309 if (selected == null) { 310 notifyResult(false); 311 return; 312 } 313 314 final IVariableBinding[] selectedBindings= (IVariableBinding[]) Arrays.asList(selected).toArray(new IVariableBinding[0]); 315 316 ITypeBinding superclass= fTypeBinding.getSuperclass(); 317 RefactoringStatus status= new RefactoringStatus(); 318 ArrayList alreadyChecked= new ArrayList (); 319 320 if (!"java.lang.Object".equals(superclass.getQualifiedName())) { status.merge(checkHashCodeEqualsExists(superclass, true)); 322 } 323 324 for (int i= 0; i < selectedBindings.length; i++) { 325 ITypeBinding fieldsType= selectedBindings[i].getType(); 326 if (fieldsType.isArray()) 327 fieldsType= fieldsType.getElementType(); 328 if (!fieldsType.isPrimitive() && !fieldsType.isEnum() && !alreadyChecked.contains(fieldsType) && !fieldsType.equals(fTypeBinding)) { 329 status.merge(checkHashCodeEqualsExists(fieldsType, false)); 330 alreadyChecked.add(fieldsType); 331 } 332 if (Modifier.isTransient(selectedBindings[i].getModifiers())) 333 status.addWarning(Messages.format(ActionMessages.GenerateHashCodeEqualsAction_transient_field_included_error, selectedBindings[i] 334 .getName()), createRefactoringStatusContext(selectedBindings[i].getJavaElement())); 335 } 336 337 if (status.hasEntries()) { 338 Dialog d= RefactoringUI.createLightWeightStatusDialog(status, getShell(), ActionMessages.GenerateHashCodeEqualsAction_error_caption); 339 if (d.open() != Window.OK) { 340 notifyResult(false); 341 return; 342 } 343 } 344 345 final CodeGenerationSettings settings= JavaPreferencesSettings.getCodeGenerationSettings(type.getJavaProject()); 346 settings.createComments= dialog.getGenerateComment(); 347 final IEditorPart editor= JavaUI.openInEditor(type.getCompilationUnit()); 348 final IRewriteTarget target= editor != null ? (IRewriteTarget) editor.getAdapter(IRewriteTarget.class) : null; 349 350 if (target != null) 351 target.beginCompoundChange(); 352 try { 353 final GenerateHashCodeEqualsOperation operation= new GenerateHashCodeEqualsOperation(fTypeBinding, selectedBindings, fUnit, dialog 354 .getElementPosition(), settings, dialog.isUseInstanceOf(), regenerate, true, false); 355 IRunnableContext context= JavaPlugin.getActiveWorkbenchWindow(); 356 if (context == null) 357 context= new BusyIndicatorRunnableContext(); 358 PlatformUI.getWorkbench().getProgressService().runInUI(context, 359 new WorkbenchRunnableAdapter(operation, operation.getSchedulingRule()), operation.getSchedulingRule()); 360 } catch (InvocationTargetException exception) { 361 ExceptionHandler.handle(exception, shell, ActionMessages.GenerateHashCodeEqualsAction_error_caption, null); 362 } catch (InterruptedException exception) { 363 } finally { 365 if (target != null) 366 target.endCompoundChange(); 367 } 368 } 369 notifyResult(dialogResult == Window.OK); 370 } 371 372 private static RefactoringStatusContext createRefactoringStatusContext(IJavaElement element) { 373 if (element instanceof IMember) { 374 return JavaStatusContext.create((IMember) element); 375 } 376 if (element instanceof ISourceReference) { 377 IOpenable openable= element.getOpenable(); 378 try { 379 if (openable instanceof ICompilationUnit) { 380 return JavaStatusContext.create((ICompilationUnit) openable, ((ISourceReference) element).getSourceRange()); 381 } else if (openable instanceof IClassFile) { 382 return JavaStatusContext.create((IClassFile) openable, ((ISourceReference) element).getSourceRange()); 383 } 384 } catch (JavaModelException e) { 385 } 387 } 388 return null; 389 } 390 391 private boolean hasHashCodeOrEquals(ITypeBinding someType) { 392 HashCodeEqualsInfo info= getTypeInfo(someType); 393 return (info.foundEquals || info.foundHashCode); 394 } 395 396 private RefactoringStatus checkHashCodeEqualsExists(ITypeBinding someType, boolean superClass) { 397 398 RefactoringStatus status= new RefactoringStatus(); 399 HashCodeEqualsInfo info= getTypeInfo(someType); 400 401 String concreteTypeWarning= superClass 402 ? ActionMessages.GenerateHashCodeEqualsAction_super_class 403 : ActionMessages.GenerateHashCodeEqualsAction_field_type; 404 String concreteMethWarning= (someType.isInterface() || Modifier.isAbstract(someType.getModifiers())) 405 ? ActionMessages.GenerateHashCodeEqualsAction_interface_does_not_declare_hashCode_equals_error 406 : ActionMessages.GenerateHashCodeEqualsAction_type_does_not_implement_hashCode_equals_error; 407 String concreteHCEWarning= null; 408 409 if (!info.foundEquals && (!info.foundHashCode)) 410 concreteHCEWarning= ActionMessages.GenerateHashCodeEqualsAction_equals_and_hashCode; 411 else if (!info.foundEquals) 412 concreteHCEWarning= ActionMessages.GenerateHashCodeEqualsAction_equals; 413 else if (!info.foundHashCode) 414 concreteHCEWarning= ActionMessages.GenerateHashCodeEqualsAction_hashCode; 415 416 if (!info.foundEquals && !info.foundHashCode) 417 status.addWarning(Messages.format(concreteMethWarning, new String [] { Messages.format(concreteTypeWarning, someType.getQualifiedName()), 418 concreteHCEWarning }), createRefactoringStatusContext(someType.getJavaElement())); 419 420 if (superClass && (info.foundFinalEquals || info.foundFinalHashCode)) { 421 status.addError(Messages.format(ActionMessages.GenerateHashCodeEqualsAction_final_hashCode_equals_in_superclass_error, Messages.format( 422 concreteTypeWarning, someType.getQualifiedName())), createRefactoringStatusContext(someType.getJavaElement())); 423 } 424 425 return status; 426 } 427 428 private HashCodeEqualsInfo getTypeInfo(ITypeBinding someType) { 429 HashCodeEqualsInfo info= new HashCodeEqualsInfo(); 430 if (someType.isTypeVariable()) { 431 someType= someType.getErasure(); 432 } 433 434 IMethodBinding[] declaredMethods= someType.getDeclaredMethods(); 435 436 for (int i= 0; i < declaredMethods.length; i++) { 437 if (declaredMethods[i].getName().equals(METHODNAME_EQUALS)) { 438 ITypeBinding[] b= declaredMethods[i].getParameterTypes(); 439 if ( (b.length == 1) && (b[0].getQualifiedName().equals("java.lang.Object"))) { info.foundEquals= true; 441 if (Modifier.isFinal(declaredMethods[i].getModifiers())) 442 info.foundFinalEquals= true; 443 } 444 } 445 if (declaredMethods[i].getName().equals(METHODNAME_HASH_CODE) && declaredMethods[i].getParameterTypes().length == 0) { 446 info.foundHashCode= true; 447 if (Modifier.isFinal(declaredMethods[i].getModifiers())) 448 449 info.foundFinalHashCode= true; 450 } 451 if (info.foundEquals && info.foundHashCode) 452 break; 453 } 454 return info; 455 } 456 457 private void initialize(IType type) throws JavaModelException { 458 RefactoringASTParser parser= new RefactoringASTParser(AST.JLS3); 459 fUnit= parser.parse(type.getCompilationUnit(), true); 460 fTypeBinding= null; 461 final AbstractTypeDeclaration declaration= (AbstractTypeDeclaration) ASTNodes.getParent(NodeFinder.perform(fUnit, type.getNameRange()), 463 AbstractTypeDeclaration.class); 464 if (declaration != null) 465 fTypeBinding= declaration.resolveBinding(); 466 467 fCandidateFields= fTypeBinding.getDeclaredFields(); 468 } 469 470 473 public void selectionChanged(IStructuredSelection selection) { 474 try { 475 setEnabled(canEnable(selection)); 476 } catch (JavaModelException exception) { 477 if (JavaModelUtil.isExceptionToBeLogged(exception)) 478 JavaPlugin.log(exception); 479 setEnabled(false); 480 } 481 } 482 483 486 public void selectionChanged(ITextSelection selection) { 487 } 489 } 490 | Popular Tags |