1 18 package org.netbeans.modules.refactoring.experimental.plugins; 19 20 import java.lang.reflect.Modifier ; 21 import java.util.ArrayList ; 22 import java.util.Collection ; 23 import java.util.HashMap ; 24 import java.util.HashSet ; 25 import java.util.Iterator ; 26 import java.util.List ; 27 import java.util.ListIterator ; 28 import java.util.Map ; 29 import java.util.Set ; 30 import javax.jmi.reflect.RefObject; 31 import org.netbeans.jmi.javamodel.*; 32 import org.netbeans.modules.javacore.internalapi.JavaMetamodel; 33 import org.netbeans.modules.refactoring.api.AbstractRefactoring; 34 import org.netbeans.modules.refactoring.api.Problem; 35 import org.netbeans.modules.refactoring.experimental.CleanUpRefactoring; 36 import org.netbeans.modules.refactoring.plugins.JavaRefactoringPlugin; 37 import org.netbeans.modules.refactoring.spi.RefactoringElementsBag; 38 import org.netbeans.modules.refactoring.spi.SimpleRefactoringElementImpl; 39 import org.openide.filesystems.FileObject; 40 import org.openide.text.PositionBounds; 41 import org.openide.util.NbBundle; 42 43 46 public class CleanUpRefactoringPlugin extends JavaRefactoringPlugin { 47 48 private CleanUpRefactoring refactoring; 49 50 public CleanUpRefactoringPlugin(CleanUpRefactoring refactoring) { 51 this.refactoring = refactoring; 52 } 53 54 public Problem preCheck() { 55 return null; 56 } 57 58 public Problem checkParameters() { 59 return null; 60 } 61 62 public Problem fastCheckParameters() { 63 if (refactoring.getResources().size() == 0) { 64 return new Problem(true, 65 NbBundle.getMessage(CleanUpRefactoringPlugin.class, "ERR_NoFiles")); } 67 if (!isRemoveUnusedCode() && !refactoring.isRemoveUnusedImports()) { 68 return new Problem(true, 69 NbBundle.getMessage(CleanUpRefactoringPlugin.class, "ERR_NoCleanUpActions")); } 71 if (refactoring.getResources().size() > 1) { 72 return new Problem(false, 73 NbBundle.getMessage(CleanUpRefactoringPlugin.class, "WARN_SingleFileRefactoring")); } else { 75 return new Problem(false, 76 NbBundle.getMessage(CleanUpRefactoringPlugin.class, "WARN_ReviewChanges")); } 78 } 79 80 public Problem prepare(RefactoringElementsBag refactoringElements) { 81 List resources = refactoring.getResources(); 82 boolean comment = refactoring.isCommentInsteadOfRemoving(); 83 boolean removeUnusedCode = isRemoveUnusedCode(); 84 boolean removeUnusedImports = refactoring.isRemoveUnusedImports(); 85 int steps = 0; 86 int sz = resources.size(); 87 if (removeUnusedCode) { 88 steps = sz * 10; 90 } 91 if (removeUnusedImports) { 92 steps += sz; 94 } 95 fireProgressListenerStart(AbstractRefactoring.PREPARE, steps); 96 try { 97 for (Iterator it = resources.iterator(); it.hasNext();) { 98 Resource res = (Resource) it.next(); 99 if (removeUnusedCode) { 100 FileObject fo = JavaMetamodel.getManager().getFileObject(res); 101 Set candidates = new HashSet (); 102 Map element2UsageList = new HashMap (); 103 collectElements(res, candidates, element2UsageList, true); 104 for (Iterator candidateIt = candidates.iterator(); candidateIt.hasNext();) { 106 Element el = (Element) candidateIt.next(); 107 if (el instanceof TypeCast) { 108 refactoringElements.add(refactoring, new RemoveUnusedElement(fo, el, null, comment)); 109 candidateIt.remove(); 110 } else { 111 List usages = (List ) element2UsageList.get(el); 112 if (usages.isEmpty()) { 113 refactoringElements.add(refactoring, new RemoveUnusedElement(fo, el, null, comment)); 114 candidateIt.remove(); 115 } 116 } 117 } 118 } 123 if (removeUnusedImports) { 124 refactoringElements.add(refactoring, new RemoveUnusedImportsElement(res)); 125 fireProgressListenerStep(); 126 } 127 } 128 } finally { 129 fireProgressListenerStop(); 130 } 131 return null; 132 } 133 134 135 private boolean isRemoveUnusedCode() { 136 boolean removeUnusedCode = 137 refactoring.isRemoveUnusedFields() || 138 refactoring.isRemoveUnusedCallableFeatures() || 139 refactoring.isRemoveUnusedClasses() || 140 refactoring.isRemoveUnusedLocalVars() || 141 refactoring.isRemoveRedundantCasts(); 142 return removeUnusedCode; 143 } 144 145 private static class RemoveUnusedImportsElement extends SimpleRefactoringElementImpl { 146 147 private Resource resource; 148 149 private String text; 150 151 public RemoveUnusedImportsElement(Resource resource) { 152 this.resource = resource; 153 this.text = NbBundle.getMessage(CleanUpRefactoringPlugin.class, "TXT_RemoveUnusedImports"); 154 } 155 156 public String getText() { 157 return text; 158 } 159 160 public String getDisplayText() { 161 return text; 162 } 163 164 public void performChange() { 165 Set types = new HashSet (); 167 collectTypes(resource, types); 168 Set importedNames = new HashSet (); 169 Set neededImportedNames = new HashSet (); 170 Import[] imports = (Import[]) resource.getImports().toArray(new Import[0]); 171 172 for (int i = 0; i < imports.length; i++) { 173 Import imp = imports[i]; 174 NamedElement importedNs = imp.getImportedNamespace(); 175 importedNames.add(importedNs.getName()); 176 } 177 178 for (Iterator typesIt = types.iterator(); typesIt.hasNext();) { 179 Type type = (Type) typesIt.next(); 180 String typeName = type.getName(); 181 String resourcePkgName = resource.getPackageName(); 182 if (resourcePkgName == null) { 183 resourcePkgName = ""; 184 } 185 String typePkgName = type.getResource().getPackageName(); 186 if (typePkgName == null) { 187 typePkgName = ""; 188 } 189 if (resourcePkgName.equals(typePkgName)) { 190 continue; 191 } 192 if (importedNames.contains(typeName)) { 193 neededImportedNames.add(typeName); 194 } else if (importedNames.contains(typePkgName)) { 195 neededImportedNames.add(typePkgName); 196 } 197 } 198 199 for (ListIterator it = resource.getImports().listIterator(); it.hasNext();) { 200 Import i = (Import) it.next(); 201 NamedElement importedNs = i.getImportedNamespace(); 202 if (importedNs instanceof ClassDefinition && "java.lang".equals(importedNs.getResource().getPackageName())) { 203 it.remove(); 204 } else if (!neededImportedNames.contains(importedNs.getName())) { 205 it.remove(); 206 } 207 } 208 } 209 210 211 private void collectTypes(Element el, Set types) { 212 Iterator it; 213 if (el instanceof ArrayReference) { 214 el = ((ArrayReference) el).getParent(); 215 } 216 if (el instanceof MultipartId) { 217 MultipartId id = (MultipartId) el; 218 List typeArgs = new ArrayList (); 219 while (id.getParent() != null) { 220 id = id.getParent(); 221 typeArgs.addAll(id.getTypeArguments()); 222 } 223 typeArgs.addAll(id.getTypeArguments()); 224 NamedElement ne = id.getElement(); 225 if (ne instanceof JavaClass) { 226 types.add(ne); 227 } 228 it = typeArgs.iterator(); 229 } else { 230 it = el.getChildren().iterator(); 231 } 232 while (it.hasNext()) { 233 collectTypes((Element) it.next(), types); 234 } 235 } 236 237 public Element getJavaElement() { 238 return resource; 239 } 240 241 public FileObject getParentFile() { 242 return JavaMetamodel.getManager().getFileObject(resource); 243 } 244 245 public PositionBounds getPosition() { 246 return null; 247 } 248 249 } 250 251 private static class RemoveUnusedElement extends SimpleRefactoringElementImpl { 252 253 private Element element; 254 255 private boolean commentOut; 256 257 private FileObject fo; 258 259 private String text; 260 261 private Collection dependencies; 262 263 public RemoveUnusedElement(FileObject fo, Element element, Collection dependencies, boolean commentOut) { 264 this.element = element; 265 this.fo = fo; 266 this.commentOut = commentOut; 267 this.dependencies = dependencies; 268 if (element instanceof TypeCast) { 269 initCastText(); 270 } else { 271 initElementText(); 272 } 273 } 274 275 public String getText() { 276 return text; 277 } 278 279 private void initCastText() { 280 String action = commentOut ? 281 NbBundle.getMessage(CleanUpRefactoringPlugin.class, "TXT_Comment") : 282 NbBundle.getMessage(CleanUpRefactoringPlugin.class, "TXT_Remove"); 283 TypeCast tc = (TypeCast) element; 284 String from = tc.getExpression().getType().getName(); 285 String to = tc.getType().getName(); 286 String code = new String (tc.getResource().getSourceText().substring( 287 tc.getStartOffset(), tc.getExpression().getEndOffset())); 288 text = NbBundle.getMessage(CleanUpRefactoringPlugin.class, "TXT_RemoveRedundantCast", 290 new Object [] { action, from, to, code}); 291 } 292 293 private void initElementText() { 294 String action = commentOut ? 295 NbBundle.getMessage(CleanUpRefactoringPlugin.class, "TXT_Comment") : 296 NbBundle.getMessage(CleanUpRefactoringPlugin.class, "TXT_Remove"); 297 String type = null; 298 if (element instanceof LocalVariable) { 299 type = NbBundle.getMessage(CleanUpRefactoringPlugin.class, "TXT_LocalVariable"); 300 } else if (element instanceof Field) { 301 type = NbBundle.getMessage(CleanUpRefactoringPlugin.class, "TXT_Field"); 302 } else if (element instanceof Method) { 303 type = NbBundle.getMessage(CleanUpRefactoringPlugin.class, "TXT_Method"); 304 } else if (element instanceof Constructor) { 305 type = NbBundle.getMessage(CleanUpRefactoringPlugin.class, "TXT_Constructor"); 306 } else if (element instanceof JavaClass) { 307 type = NbBundle.getMessage(CleanUpRefactoringPlugin.class, "TXT_Class"); 308 } 309 String name = getDisplayName((NamedElement) element); 310 text = NbBundle.getMessage(CleanUpRefactoringPlugin.class, 312 "TXT_RemoveUnusedElement", new Object [] { action, type, name, }); 314 } 315 316 public String getDisplayText() { 317 return text; 318 } 319 320 private String getDisplayName(NamedElement ne) { 321 if (ne instanceof CallableFeature) { 322 String name; 323 if (ne instanceof Constructor) { 324 name = ((JavaClass) ((Constructor) ne).getDeclaringClass()).getSimpleName(); 325 } else { 326 name = ne.getName(); 327 } 328 StringBuffer buff = new StringBuffer (name).append('('); 329 Parameter[] p = (Parameter[]) ((CallableFeature) ne).getParameters().toArray(new Parameter[0]); 330 for (int i = 0; i < p.length; i++) { 331 if (i > 0) { 332 buff.append(", "); } 334 buff.append(p[i].getType().getName()); 335 } 336 buff.append(')'); 337 return buff.toString(); 338 } else if (ne instanceof JavaClass) { 339 return ((JavaClass) ne).getSimpleName(); 340 } else { 341 return ne.getName(); 342 } 343 } 344 345 public void performChange() { 346 if (dependencies != null) { 347 for (Iterator it = dependencies.iterator(); it.hasNext();) { 348 Element el = (Element) it.next(); 349 if (el.isValid()) { 350 return; 351 } 352 } 353 } 354 if (element instanceof ClassMember) { 355 if (commentOut) { 356 } else { 358 ClassMember cm = (ClassMember) element; 359 cm.getDeclaringClass().replaceChild(cm, null); 360 } 361 } else if (element instanceof LocalVariable) { 362 if (commentOut) { 363 } else { 365 LocalVariable lv = (LocalVariable) element; 366 LocalVarDeclaration lvd = (LocalVarDeclaration) lv.refImmediateComposite(); 367 List vars = lvd.getVariables(); 368 if (vars.size() > 1) { 369 vars.remove(lv); 370 } else { 371 StatementBlock sb = (StatementBlock) lvd.refImmediateComposite(); 372 sb.getStatements().remove(lvd); 373 } 374 } 375 } else if (element instanceof TypeCast) { 376 TypeCast tc = (TypeCast) element; 377 Object parent = tc.refImmediateComposite(); 378 while (parent != null && !(parent instanceof Element)) { 379 if (!(parent instanceof RefObject)) { 380 continue; 381 } 382 parent = ((RefObject) parent).refImmediateComposite(); 383 } 384 if (parent != null) { 385 Element parentEl = (Element) parent; 386 Expression clone = (Expression) tc.getExpression().duplicate(); 387 parentEl.replaceChild(tc, clone); 388 } 389 } 390 } 391 392 public Element getJavaElement() { 393 if (element instanceof ClassMember) { 394 return ((ClassMember) element).getDeclaringClass(); 395 } else if (element instanceof LocalVariable) { 396 Object parent = element; 397 while (parent != null && !(parent instanceof CallableFeature) && (parent instanceof RefObject)) { 398 parent = ((RefObject) parent).refImmediateComposite(); 399 } 400 return parent != null ? (Element) parent : element.getResource(); 401 } else if (element instanceof TypeCast) { 402 Object parent = element; 403 while (parent != null && !(parent instanceof ClassMember) && (parent instanceof RefObject)) { 404 parent = ((RefObject) parent).refImmediateComposite(); 405 } 406 return parent != null ? (Element) parent : element.getResource(); 407 } else { 408 return element.getResource(); 409 } 410 } 411 412 public FileObject getParentFile() { 413 return fo; 414 } 415 416 public PositionBounds getPosition() { 417 return null; 418 } 419 420 } 421 422 423 private void collectElements( 424 Element el, Set removalCandidates, Map element2UsageList, boolean fireProgress) { 425 if (isRemovalCandidate(el, refactoring)) { 426 addRemovalCandidate(el, null, removalCandidates, element2UsageList); 427 } else if (el instanceof ElementReference) { 428 Element actualElement = ((ElementReference) el).getElement(); 429 if (isRemovalCandidate(actualElement, refactoring)) { 430 addRemovalCandidate(actualElement, el, removalCandidates, element2UsageList); 431 } 432 } 433 Element[] children = (Element[]) el.getChildren().toArray(new Element[0]); 434 double currentStep = 0; 435 int lastStep = 0; 436 try { 437 double increment = 10d / children.length; 440 for (int i = 0; i < children.length; i++) { 441 if (fireProgress) { 442 currentStep += increment; 443 if ((int) currentStep != lastStep) { 444 fireProgressListenerStep((int) currentStep - lastStep); 445 lastStep = (int) currentStep; 446 } 447 } 448 collectElements(children[i], removalCandidates, element2UsageList, false); 449 } 450 } finally { 451 if (fireProgress && lastStep < 10) { 453 fireProgressListenerStep(10 - lastStep); 454 } 455 } 456 } 457 458 private void addRemovalCandidate(Element candidate, Element user, 459 Set removalCandidates, Map element2UsageList) { 460 removalCandidates.add(candidate); 461 addElementUser(candidate, user, element2UsageList); 462 } 463 464 private void addElementUser(Element element, Element user, Map element2UsageList) { 465 List users = (List ) element2UsageList.get(element); 466 if (users == null) { 467 users = new ArrayList (); 468 element2UsageList.put(element, users); 469 } 470 if (user != null) { 471 users.add(user); 472 } 473 } 474 475 private boolean isRemovalCandidate(Element element, CleanUpRefactoring refactoring) { 476 if (element instanceof Field) { 477 return isRemovableField((Field) element); 478 } else if (element instanceof Method) { 479 boolean b = refactoring.isRemoveUnusedCallableFeatures() && (((Method) element).getModifiers() & Modifier.PRIVATE) != 0; 481 if (!b) { 482 return false; 483 } 484 return !isSerializationMethod((Method) element); 486 } else if (element instanceof Constructor) { 487 Constructor constr = (Constructor) element; 489 return refactoring.isRemoveUnusedCallableFeatures() && ((constr.getModifiers() & Modifier.PRIVATE) != 0) && !constr.getParameters().isEmpty(); 491 } else if (element instanceof JavaClass) { 492 return refactoring.isRemoveUnusedClasses() && (((JavaClass) element).getModifiers() & Modifier.PRIVATE) != 0; 494 } else if (element instanceof LocalVariable) { 495 return isRemovableLocalVariable((LocalVariable) element); 496 } else if (element instanceof TypeCast) { 497 TypeCast tc = (TypeCast) element; 499 return refactoring.isRemoveRedundantCasts() && isCastRedundant(tc.getExpression().getType(), tc.getType()); 500 } 501 return false; 502 } 503 504 private boolean isSerializationMethod(Method m) { 505 String name = m.getName(); 506 boolean ro = "readObject".equals(name); boolean wo = "writeObject".equals(name); if (!(ro || wo)) { 509 return false; 510 } 511 Parameter[] p = (Parameter[]) m.getParameters().toArray(new Parameter[0]); 512 if (p.length != 1) { 513 return false; 514 } 515 if (ro) { 516 return "java.io.ObjectInputStream".equals(p[0].getType().getName()); } else { 518 return "java.io.ObjectOutputStream".equals(p[0].getType().getName()); } 520 } 521 522 525 private boolean isRemovableField(Field f) { 526 if (!refactoring.isRemoveUnusedFields()) { 527 return false; 528 } 529 if ((f.getModifiers() & Modifier.PRIVATE) == 0) { 530 return false; 531 } 532 if ("serialVersionUID".equals(f.getName())) { return false; 534 } 535 return isElementWithInitialValueRemovable(f.getInitialValue()); 536 } 537 538 543 private boolean isElementWithInitialValueRemovable(InitialValue val) { 544 if (val == null) { 545 return true; 546 } 547 if (val instanceof Literal) { 548 return true; 549 } 550 if (val instanceof VariableAccess) { 551 return true; 552 } 553 return false; 554 } 555 556 559 private boolean isRemovableLocalVariable(LocalVariable var) { 560 if (!refactoring.isRemoveUnusedLocalVars()) { 561 return false; 562 } 563 return isElementWithInitialValueRemovable(var.getInitialValue()); 564 } 565 566 private boolean isCastRedundant(Type fromType, Type toType) { 567 if (fromType instanceof PrimitiveType && toType instanceof PrimitiveType) { 568 } else if (fromType instanceof ParameterizedType && toType instanceof ClassDefinition) { 570 ClassDefinition fromClass = ((ParameterizedType) fromType).getDefinition(); 571 ClassDefinition toClass = (ClassDefinition) toType; 572 if (fromClass.isSubTypeOf(toClass)) { 573 return true; 574 } 575 } else if (fromType instanceof ClassDefinition && toType instanceof ClassDefinition) { 576 ClassDefinition fromClass = (ClassDefinition) fromType; 577 ClassDefinition toClass = (ClassDefinition) toType; 578 if (fromClass.isSubTypeOf(toClass)) { 579 return true; 580 } 581 } 582 return false; 583 } 584 585 } 586 | Popular Tags |