PageRenderTime 64ms CodeModel.GetById 16ms RepoModel.GetById 0ms app.codeStats 0ms

/projects/netbeans-7.3/java.hints.ui/src/org/netbeans/modules/java/hints/spiimpl/refactoring/AbstractApplyHintsRefactoringPlugin.java

https://gitlab.com/essere.lab.public/qualitas.class-corpus
Java | 631 lines | 442 code | 91 blank | 98 comment | 65 complexity | 56a069f5c0ffc2aa7214c371f5683dd6 MD5 | raw file
  1. /*
  2. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
  3. *
  4. * Copyright 2010 Oracle and/or its affiliates. All rights reserved.
  5. *
  6. * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
  7. * Other names may be trademarks of their respective owners.
  8. *
  9. * The contents of this file are subject to the terms of either the GNU
  10. * General Public License Version 2 only ("GPL") or the Common
  11. * Development and Distribution License("CDDL") (collectively, the
  12. * "License"). You may not use this file except in compliance with the
  13. * License. You can obtain a copy of the License at
  14. * http://www.netbeans.org/cddl-gplv2.html
  15. * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
  16. * specific language governing permissions and limitations under the
  17. * License. When distributing the software, include this License Header
  18. * Notice in each file and include the License file at
  19. * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this
  20. * particular file as subject to the "Classpath" exception as provided
  21. * by Oracle in the GPL Version 2 section of the License file that
  22. * accompanied this code. If applicable, add the following below the
  23. * License Header, with the fields enclosed by brackets [] replaced by
  24. * your own identifying information:
  25. * "Portions Copyrighted [year] [name of copyright owner]"
  26. *
  27. * If you wish your version of this file to be governed by only the CDDL
  28. * or only the GPL Version 2, indicate your decision by adding
  29. * "[Contributor] elects to include this software in this distribution
  30. * under the [CDDL or GPL Version 2] license." If you do not indicate a
  31. * single choice of license, a recipient has the option to distribute
  32. * your version of this file under either the CDDL, the GPL Version 2 or
  33. * to extend the choice of license to its licensees as provided above.
  34. * However, if you add GPL Version 2 code and therefore, elected the GPL
  35. * Version 2 license, then the option applies only if the new code is
  36. * made subject to such option by the copyright holder.
  37. *
  38. * Contributor(s):
  39. *
  40. * Portions Copyrighted 2010 Sun Microsystems, Inc.
  41. */
  42. package org.netbeans.modules.java.hints.spiimpl.refactoring;
  43. import java.io.IOException;
  44. import java.lang.ref.WeakReference;
  45. import java.util.ArrayList;
  46. import java.util.Collection;
  47. import java.util.Collections;
  48. import java.util.Comparator;
  49. import java.util.HashMap;
  50. import java.util.HashSet;
  51. import java.util.IdentityHashMap;
  52. import java.util.Iterator;
  53. import java.util.LinkedList;
  54. import java.util.List;
  55. import java.util.Map;
  56. import java.util.Map.Entry;
  57. import java.util.Set;
  58. import java.util.TreeMap;
  59. import java.util.concurrent.atomic.AtomicBoolean;
  60. import java.util.logging.Level;
  61. import java.util.logging.Logger;
  62. import org.netbeans.api.java.source.CompilationController;
  63. import org.netbeans.api.java.source.ModificationResult;
  64. import org.netbeans.api.java.source.ModificationResult.Difference;
  65. import org.netbeans.modules.java.hints.spiimpl.MessageImpl;
  66. import org.netbeans.modules.java.hints.spiimpl.batch.BatchSearch;
  67. import org.netbeans.modules.java.hints.spiimpl.batch.BatchSearch.BatchResult;
  68. import org.netbeans.modules.java.hints.spiimpl.batch.BatchSearch.Resource;
  69. import org.netbeans.modules.java.hints.spiimpl.batch.BatchSearch.Scope;
  70. import org.netbeans.modules.java.hints.spiimpl.batch.BatchUtilities;
  71. import org.netbeans.modules.java.hints.spiimpl.batch.ProgressHandleWrapper;
  72. import org.netbeans.modules.java.hints.spiimpl.batch.ProgressHandleWrapper.ProgressHandleAbstraction;
  73. import org.netbeans.spi.java.hints.HintContext.MessageKind;
  74. import org.netbeans.modules.java.hints.providers.spi.HintDescription;
  75. import org.netbeans.modules.refactoring.api.AbstractRefactoring;
  76. import org.netbeans.modules.refactoring.api.Problem;
  77. import org.netbeans.modules.refactoring.api.ProgressEvent;
  78. import org.netbeans.modules.refactoring.api.ProgressListener;
  79. import org.netbeans.modules.refactoring.java.spi.JavaRefactoringPlugin;
  80. import org.netbeans.modules.refactoring.spi.ProgressProvider;
  81. import org.netbeans.modules.refactoring.spi.ProgressProviderAdapter;
  82. import org.netbeans.modules.refactoring.spi.RefactoringElementImplementation;
  83. import org.netbeans.modules.refactoring.spi.RefactoringElementsBag;
  84. import org.netbeans.modules.refactoring.spi.RefactoringPlugin;
  85. import org.netbeans.modules.refactoring.spi.SimpleRefactoringElementImplementation;
  86. import org.netbeans.modules.refactoring.spi.Transaction;
  87. import org.netbeans.spi.editor.hints.ErrorDescription;
  88. import org.netbeans.spi.java.hints.JavaFix;
  89. import org.openide.filesystems.FileObject;
  90. import org.openide.text.PositionBounds;
  91. import org.openide.text.PositionRef;
  92. import org.openide.util.Exceptions;
  93. import org.openide.util.Lookup;
  94. /**
  95. *
  96. * @author lahvac
  97. */
  98. public abstract class AbstractApplyHintsRefactoringPlugin extends ProgressProviderAdapter implements RefactoringPlugin, ProgressHandleAbstraction {
  99. private final AbstractRefactoring refactoring;
  100. private Comparator<FileObject> FILE_COMPARATOR = new Comparator<FileObject>() {
  101. @Override public int compare(FileObject o1, FileObject o2) {
  102. return o1.getPath().compareTo(o2.getPath());
  103. }
  104. };
  105. protected final AtomicBoolean cancel = new AtomicBoolean();
  106. protected AbstractApplyHintsRefactoringPlugin(AbstractRefactoring refactoring) {
  107. this.refactoring = refactoring;
  108. }
  109. public void cancelRequest() {
  110. cancel.set(true);
  111. }
  112. protected final Problem messagesToProblem(Collection<MessageImpl> problems) throws IllegalStateException {
  113. Problem current = null;
  114. for (MessageImpl problem : problems) {
  115. Problem p = new Problem(problem.kind == MessageKind.ERROR, problem.text);
  116. if (current != null)
  117. p.setNext(current);
  118. current = p;
  119. }
  120. return current;
  121. }
  122. protected Collection<MessageImpl> performApplyPattern(Iterable<? extends HintDescription> pattern, Scope scope, RefactoringElementsBag refactoringElements) {
  123. ProgressHandleWrapper w = new ProgressHandleWrapper(this, 30, 70);
  124. BatchResult candidates = BatchSearch.findOccurrences(pattern, scope, w);
  125. Collection<RefactoringElementImplementation> fileChanges = new ArrayList<RefactoringElementImplementation>();
  126. Collection<MessageImpl> problems = new LinkedList<MessageImpl>(candidates.problems);
  127. Map<JavaFix, ModificationResult> changesPerFix = new IdentityHashMap<JavaFix, ModificationResult>();
  128. Collection<? extends ModificationResult> res = BatchUtilities.applyFixes(candidates, w, cancel, fileChanges, changesPerFix, problems);
  129. Set<ModificationResult> enabled = Collections.newSetFromMap(new IdentityHashMap<ModificationResult, Boolean>());
  130. Map<FileObject, Map<JavaFix, ModificationResult>> file2Fixes2Changes = new HashMap<FileObject, Map<JavaFix, ModificationResult>>();
  131. Map<FileObject, Set<FileObject>> affectedFiles = new HashMap<FileObject, Set<FileObject>>();
  132. Map<FileObject, List<RefactoringElementImplementation>> file2Changes = new TreeMap<FileObject, List<RefactoringElementImplementation>>(FILE_COMPARATOR);
  133. for (Entry<JavaFix, ModificationResult> changesPerFixEntry : changesPerFix.entrySet()) {
  134. enabled.add(changesPerFixEntry.getValue());
  135. for (FileObject file : changesPerFixEntry.getValue().getModifiedFileObjects()) {
  136. List<RefactoringElementImplementation> currentFileChanges = file2Changes.get(file);
  137. if (currentFileChanges == null) {
  138. file2Changes.put(file, currentFileChanges = new ArrayList<RefactoringElementImplementation>());
  139. }
  140. currentFileChanges.add(new ModificationResultElement(file, changesPerFixEntry.getKey(), changesPerFixEntry.getValue(), enabled));
  141. Map<JavaFix, ModificationResult> perFile = file2Fixes2Changes.get(file);
  142. if (perFile == null) {
  143. file2Fixes2Changes.put(file, perFile = new IdentityHashMap<JavaFix, ModificationResult>());
  144. }
  145. perFile.put(changesPerFixEntry.getKey(), changesPerFixEntry.getValue());
  146. Set<FileObject> aff = affectedFiles.get(file);
  147. if (aff == null) {
  148. affectedFiles.put(file, aff = new HashSet<FileObject>());
  149. }
  150. aff.addAll(changesPerFixEntry.getValue().getModifiedFileObjects());
  151. }
  152. }
  153. for (List<RefactoringElementImplementation> changes : file2Changes.values()) {
  154. Collections.sort(changes, new Comparator<RefactoringElementImplementation>() {
  155. @Override public int compare(RefactoringElementImplementation o1, RefactoringElementImplementation o2) {
  156. return o1.getPosition().getBegin().getOffset() - o2.getPosition().getBegin().getOffset();
  157. }
  158. });
  159. refactoringElements.addAll(refactoring, changes);
  160. }
  161. refactoringElements.registerTransaction(new DelegatingTransaction(enabled, file2Fixes2Changes, affectedFiles, res));
  162. for (RefactoringElementImplementation fileChange : fileChanges) {
  163. refactoringElements.addFileChange(refactoring, fileChange);
  164. }
  165. w.finish();
  166. return problems;
  167. }
  168. protected final void prepareElements(BatchResult candidates, ProgressHandleWrapper w, final RefactoringElementsBag refactoringElements, final boolean verify, List<MessageImpl> problems) {
  169. final Map<FileObject, Collection<RefactoringElementImplementation>> file2Changes = new TreeMap<FileObject, Collection<RefactoringElementImplementation>>(FILE_COMPARATOR);
  170. if (verify) {
  171. BatchSearch.getVerifiedSpans(candidates, w, new BatchSearch.VerifiedSpansCallBack() {
  172. public void groupStarted() {}
  173. public boolean spansVerified(CompilationController wc, Resource r, Collection<? extends ErrorDescription> hints) throws Exception {
  174. List<PositionBounds> spans = new LinkedList<PositionBounds>();
  175. for (ErrorDescription ed : hints) {
  176. spans.add(ed.getRange());
  177. }
  178. file2Changes.put(r.getResolvedFile(), Utilities.createRefactoringElementImplementation(r.getResolvedFile(), spans, verify));
  179. return true;
  180. }
  181. public void groupFinished() {}
  182. public void cannotVerifySpan(Resource r) {
  183. file2Changes.put(r.getResolvedFile(), Utilities.createRefactoringElementImplementation(r.getResolvedFile(), prepareSpansFor(r), verify));
  184. }
  185. }, problems, cancel);
  186. } else {
  187. int[] parts = new int[candidates.getResources().size()];
  188. int index = 0;
  189. for (Collection<? extends Resource> resources : candidates.getResources()) {
  190. parts[index++] = resources.size();
  191. }
  192. ProgressHandleWrapper inner = w.startNextPartWithEmbedding(parts);
  193. for (Collection<? extends Resource> it :candidates.getResources()) {
  194. inner.startNextPart(it.size());
  195. for (Resource r : it) {
  196. file2Changes.put(r.getResolvedFile(), Utilities.createRefactoringElementImplementation(r.getResolvedFile(), prepareSpansFor(r), verify));
  197. inner.tick();
  198. }
  199. }
  200. }
  201. for (Collection<RefactoringElementImplementation> res : file2Changes.values()) {
  202. refactoringElements.addAll(refactoring, res);
  203. }
  204. }
  205. private static List<PositionBounds> prepareSpansFor(Resource r) {
  206. return Utilities.prepareSpansFor(r.getResolvedFile(), r.getCandidateSpans());
  207. }
  208. public void start(int totalWork) {
  209. fireProgressListenerStart(-1, totalWork);
  210. lastWorkDone = 0;
  211. }
  212. private int lastWorkDone;
  213. public void progress(int currentWorkDone) {
  214. while (lastWorkDone < currentWorkDone) {
  215. fireProgressListenerStep(currentWorkDone);
  216. lastWorkDone++;
  217. }
  218. }
  219. public void progress(String message) {
  220. //ignored
  221. }
  222. public void finish() {
  223. fireProgressListenerStop();
  224. }
  225. private static final class ModificationResultElement extends SimpleRefactoringElementImplementation {
  226. private PositionBounds bounds;
  227. private String displayText;
  228. private FileObject parentFile;
  229. private ModificationResult modification;
  230. private WeakReference<String> newFileContent;
  231. private final Set<ModificationResult> enabledResults;
  232. private ModificationResultElement(FileObject parentFile, JavaFix jf, ModificationResult modification, Set<ModificationResult> enabledResults) {
  233. PositionRef s = modification.getDifferences(parentFile).iterator().next().getStartPosition();
  234. this.bounds = new PositionBounds(s, s);
  235. this.displayText = jf.toEditorFix().getText();
  236. this.parentFile = parentFile;
  237. this.modification = modification;
  238. this.enabledResults = enabledResults;
  239. }
  240. @Override
  241. public String getDisplayText() {
  242. return displayText;
  243. }
  244. @Override
  245. public Lookup getLookup() {
  246. return Lookup.EMPTY;
  247. }
  248. @Override
  249. public void setEnabled(boolean enabled) {
  250. if (enabled) {
  251. enabledResults.add(modification);
  252. } else {
  253. enabledResults.remove(modification);
  254. }
  255. super.setEnabled(enabled);
  256. }
  257. @Override
  258. public boolean isEnabled() {
  259. return enabledResults.contains(modification);
  260. }
  261. @Override
  262. public PositionBounds getPosition() {
  263. return bounds;
  264. }
  265. @Override
  266. public String getText() {
  267. return displayText;
  268. }
  269. @Override
  270. public void performChange() {
  271. }
  272. @Override
  273. public FileObject getParentFile() {
  274. return parentFile;
  275. }
  276. @Override
  277. protected String getNewFileContent() {
  278. String result = newFileContent != null ? newFileContent.get() : null;
  279. if (result != null) return result;
  280. try {
  281. result = modification.getResultingSource(parentFile);
  282. } catch (IOException ex) {
  283. Exceptions.printStackTrace(ex);
  284. return null;
  285. }
  286. newFileContent = new WeakReference<String>(result);
  287. return result;
  288. }
  289. }
  290. private static final class DelegatingTransaction implements Transaction, ProgressProvider {
  291. private final Set<ModificationResult> enabled;
  292. private final Map<FileObject, Map<JavaFix, ModificationResult>> file2Fixes2Changes;
  293. private final Map<FileObject, Set<FileObject>> affectedFiles;
  294. private final Collection<? extends ModificationResult> completeModificationResult;
  295. private Transaction delegate;
  296. private ProgressSupport progressSupport;
  297. private final ProgressListener listener;
  298. public DelegatingTransaction(Set<ModificationResult> enabled, Map<FileObject, Map<JavaFix, ModificationResult>> file2Fixes2Changes, Map<FileObject, Set<FileObject>> affectedFiles, Collection<? extends ModificationResult> completeModificationResult) {
  299. this.enabled = enabled;
  300. this.file2Fixes2Changes = file2Fixes2Changes;
  301. this.affectedFiles = affectedFiles;
  302. this.completeModificationResult = completeModificationResult;
  303. listener = new ProgressListener() {
  304. @Override
  305. public void start(ProgressEvent event) {
  306. fireProgressListenerStart(event.getOperationType(), event.getCount());
  307. }
  308. @Override
  309. public void step(ProgressEvent event) {
  310. fireProgressListenerStep(event.getCount());
  311. }
  312. @Override
  313. public void stop(ProgressEvent event) {
  314. fireProgressListenerStop();
  315. }
  316. };
  317. }
  318. @Override
  319. public synchronized void commit() {
  320. if (delegate == null) {
  321. Set<FileObject> toRecompute = new HashSet<FileObject>();
  322. for (ModificationResult mr : completeModificationResult) {
  323. for (FileObject modified : mr.getModifiedFileObjects()) {
  324. if (!affectedFiles.containsKey(modified)) {
  325. assert mr.getDifferences(modified).isEmpty();
  326. continue;
  327. }
  328. for (FileObject affected : affectedFiles.get(modified)) {
  329. for (Entry<JavaFix, ModificationResult> e : file2Fixes2Changes.get(affected).entrySet()) {
  330. if (!enabled.contains(e.getValue())) {
  331. toRecompute.add(affected);
  332. break;
  333. }
  334. }
  335. }
  336. }
  337. }
  338. final Map<FileObject, Collection<JavaFix>> toRun = new HashMap<FileObject, Collection<JavaFix>>();
  339. for (Iterator<FileObject> it = toRecompute.iterator(); it.hasNext();) {
  340. FileObject r = it.next();
  341. for (ModificationResult mr : completeModificationResult) {
  342. List<? extends Difference> diffs = mr.getDifferences(r);
  343. if (diffs == null) continue;
  344. for (Difference c : diffs) {
  345. c.exclude(true);
  346. }
  347. }
  348. for (Entry<JavaFix, ModificationResult> e : file2Fixes2Changes.get(r).entrySet()) {
  349. if (enabled.contains(e.getValue())) {
  350. Collection<JavaFix> fixes2Run = toRun.get(r);
  351. if (fixes2Run == null) {
  352. toRun.put(r, fixes2Run = new ArrayList<JavaFix>());
  353. }
  354. fixes2Run.add(e.getKey());
  355. }
  356. }
  357. }
  358. List<ModificationResult> real = new ArrayList<ModificationResult>(completeModificationResult);
  359. real.addAll(BatchUtilities.applyFixes(toRun));
  360. delegate = JavaRefactoringPlugin.createTransaction(new LinkedList<ModificationResult>(real));
  361. }
  362. if(delegate instanceof ProgressProvider) {
  363. ProgressProvider progressProvider = (ProgressProvider) delegate;
  364. progressProvider.addProgressListener(listener);
  365. }
  366. try {
  367. delegate.commit();
  368. } finally {
  369. if(delegate instanceof ProgressProvider) {
  370. ProgressProvider progressProvider = (ProgressProvider) delegate;
  371. progressProvider.removeProgressListener(listener);
  372. }
  373. }
  374. }
  375. @Override
  376. public synchronized void rollback() {
  377. delegate.rollback();
  378. }
  379. /**
  380. * Registers ProgressListener to receive events.
  381. *
  382. * @param listener The listener to register.
  383. *
  384. */
  385. @Override
  386. public synchronized void addProgressListener(ProgressListener listener) {
  387. if (progressSupport == null) {
  388. progressSupport = new ProgressSupport();
  389. }
  390. progressSupport.addProgressListener(listener);
  391. }
  392. /**
  393. * Removes ProgressListener from the list of listeners.
  394. *
  395. * @param listener The listener to remove.
  396. *
  397. */
  398. @Override
  399. public synchronized void removeProgressListener(ProgressListener listener) {
  400. if (progressSupport != null) {
  401. progressSupport.removeProgressListener(listener);
  402. }
  403. }
  404. private void fireProgressListenerStart(int type, int count) {
  405. if (progressSupport != null) {
  406. progressSupport.fireProgressListenerStart(this, type, count);
  407. }
  408. }
  409. private void fireProgressListenerStep(int count) {
  410. if (progressSupport != null) {
  411. progressSupport.fireProgressListenerStep(this, count);
  412. }
  413. }
  414. private void fireProgressListenerStop() {
  415. if (progressSupport != null) {
  416. progressSupport.fireProgressListenerStop(this);
  417. }
  418. }
  419. /**
  420. * Support class for progress notifications.
  421. * Copy of org.netbeans.modules.refactoring.api.impl.ProgressSupport
  422. * @author Martin Matula, Jan Becicka
  423. */
  424. public final class ProgressSupport {
  425. /**
  426. * Utility field holding list of ProgressListeners.
  427. */
  428. private final List<ProgressListener> progressListenerList = new ArrayList<ProgressListener>();
  429. private int counter;
  430. private boolean deterministic;
  431. public boolean isEmpty() {
  432. return progressListenerList.isEmpty();
  433. }
  434. public synchronized void addProgressListener(ProgressListener listener) {
  435. progressListenerList.add(listener);
  436. }
  437. /**
  438. * Removes ProgressListener from the list of listeners.
  439. *
  440. * @param listener The listener to remove.
  441. *
  442. */
  443. public synchronized void removeProgressListener(ProgressListener listener) {
  444. progressListenerList.remove(listener);
  445. }
  446. /**
  447. * Notifies all registered listeners about the event.
  448. *
  449. * @param type Type of operation that is starting.
  450. * @param count Number of steps the operation consists of.
  451. *
  452. */
  453. public void fireProgressListenerStart(Object source, int type, int count) {
  454. counter = -1;
  455. deterministic = count > 0;
  456. ProgressEvent event = new ProgressEvent(source, ProgressEvent.START, type, count);
  457. ProgressListener[] listeners = getListenersCopy();
  458. for (ProgressListener listener : listeners) {
  459. try {
  460. listener.start(event);
  461. } catch (RuntimeException e) {
  462. log(e);
  463. }
  464. }
  465. }
  466. /**
  467. * Notifies all registered listeners about the event.
  468. *
  469. * @param type Type of operation that is starting.
  470. * @param count Number of steps the operation consists of.
  471. *
  472. */
  473. public void fireProgressListenerStart(int type, int count) {
  474. fireProgressListenerStart(this, type, count);
  475. }
  476. /**
  477. * Notifies all registered listeners about the event.
  478. */
  479. public void fireProgressListenerStep(Object source, int count) {
  480. if (deterministic) {
  481. if (count < 0) {
  482. deterministic = false;
  483. }
  484. counter = count;
  485. } else {
  486. if (count > 0) {
  487. deterministic = true;
  488. counter = -1;
  489. } else {
  490. counter = count;
  491. }
  492. }
  493. ProgressEvent event = new ProgressEvent(source, ProgressEvent.STEP, 0, count);
  494. ProgressListener[] listeners = getListenersCopy();
  495. for (ProgressListener listener : listeners) {
  496. try {
  497. listener.step(event);
  498. } catch (RuntimeException e) {
  499. log(e);
  500. }
  501. }
  502. }
  503. /**
  504. * Notifies all registered listeners about the event.
  505. */
  506. public void fireProgressListenerStep(Object source) {
  507. if (deterministic) {
  508. ++counter;
  509. }
  510. fireProgressListenerStep(source, counter);
  511. }
  512. /**
  513. * Notifies all registered listeners about the event.
  514. */
  515. public void fireProgressListenerStop(Object source) {
  516. ProgressEvent event = new ProgressEvent(source, ProgressEvent.STOP);
  517. ProgressListener[] listeners = getListenersCopy();
  518. for (ProgressListener listener : listeners) {
  519. try {
  520. listener.stop(event);
  521. } catch (RuntimeException e) {
  522. log(e);
  523. }
  524. }
  525. }
  526. /**
  527. * Notifies all registered listeners about the event.
  528. */
  529. public void fireProgressListenerStop() {
  530. fireProgressListenerStop(this);
  531. }
  532. private synchronized ProgressListener[] getListenersCopy() {
  533. return progressListenerList.toArray(new ProgressListener[progressListenerList.size()]);
  534. }
  535. private void log(Exception e) {
  536. Logger.getLogger(ProgressSupport.class.getName()).log(Level.INFO, e.getMessage(), e);
  537. }
  538. }
  539. }
  540. }