blob: ba35ccbdc066543242b4c5892b03d65989b1f087 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2006 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.jdt.internal.ui.text.correction;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.MultiStatus;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.SubProgressMonitor;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IMarker;
import org.eclipse.core.resources.IResource;
import org.eclipse.swt.graphics.Image;
import org.eclipse.jface.dialogs.ErrorDialog;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.Position;
import org.eclipse.jface.text.source.IAnnotationModel;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.IMarkerResolution;
import org.eclipse.ui.IMarkerResolutionGenerator;
import org.eclipse.ui.IMarkerResolutionGenerator2;
import org.eclipse.ui.texteditor.ITextEditor;
import org.eclipse.ui.texteditor.MarkerUtilities;
import org.eclipse.ui.views.markers.WorkbenchMarkerResolution;
import org.eclipse.ltk.core.refactoring.Change;
import org.eclipse.ltk.core.refactoring.CompositeChange;
import org.eclipse.ltk.core.refactoring.PerformChangeOperation;
import org.eclipse.ltk.core.refactoring.RefactoringCore;
import org.eclipse.ltk.core.refactoring.RefactoringStatus;
import org.eclipse.ltk.core.refactoring.RefactoringStatusEntry;
import org.eclipse.ltk.core.refactoring.TextChange;
import org.eclipse.jdt.core.CorrectionEngine;
import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.IJavaModelMarker;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.core.compiler.IProblem;
import org.eclipse.jdt.core.dom.ASTParser;
import org.eclipse.jdt.core.dom.ASTRequestor;
import org.eclipse.jdt.core.dom.CompilationUnit;
import org.eclipse.jdt.internal.corext.fix.IFix;
import org.eclipse.jdt.internal.corext.refactoring.Checks;
import org.eclipse.jdt.internal.corext.refactoring.changes.CompilationUnitChange;
import org.eclipse.jdt.internal.corext.refactoring.changes.MultiStateCompilationUnitChange;
import org.eclipse.jdt.ui.JavaUI;
import org.eclipse.jdt.ui.text.java.CompletionProposalComparator;
import org.eclipse.jdt.ui.text.java.IInvocationContext;
import org.eclipse.jdt.ui.text.java.IJavaCompletionProposal;
import org.eclipse.jdt.ui.text.java.IProblemLocation;
import org.eclipse.jdt.internal.ui.JavaPlugin;
import org.eclipse.jdt.internal.ui.fix.ICleanUp;
import org.eclipse.jdt.internal.ui.javaeditor.ASTProvider;
import org.eclipse.jdt.internal.ui.javaeditor.EditorUtility;
import org.eclipse.jdt.internal.ui.javaeditor.JavaMarkerAnnotation;
/**
*/
public class CorrectionMarkerResolutionGenerator implements IMarkerResolutionGenerator, IMarkerResolutionGenerator2 {
public static class CorrectionMarkerResolution extends WorkbenchMarkerResolution {
private static final IMarker[] NO_MARKERS= new IMarker[0];
private static final int BATCH_SIZE= 40;
private ICompilationUnit fCompilationUnit;
private int fOffset;
private int fLength;
private IJavaCompletionProposal fProposal;
private final IMarker fMarker;
/**
* Constructor for CorrectionMarkerResolution.
* @param marker
*/
public CorrectionMarkerResolution(ICompilationUnit cu, int offset, int length, IJavaCompletionProposal proposal, IMarker marker) {
fCompilationUnit= cu;
fOffset= offset;
fLength= length;
fProposal= proposal;
fMarker= marker;
}
/* (non-Javadoc)
* @see IMarkerResolution#getLabel()
*/
public String getLabel() {
return fProposal.getDisplayString();
}
/* (non-Javadoc)
* @see IMarkerResolution#run(IMarker)
*/
public void run(IMarker marker) {
try {
IEditorPart part= EditorUtility.isOpenInEditor(fCompilationUnit);
if (part == null) {
part= EditorUtility.openInEditor(fCompilationUnit);
if (part instanceof ITextEditor) {
((ITextEditor) part).selectAndReveal(fOffset, fLength);
}
}
if (part != null) {
IEditorInput input= part.getEditorInput();
IDocument doc= JavaPlugin.getDefault().getCompilationUnitDocumentProvider().getDocument(input);
fProposal.apply(doc);
}
} catch (CoreException e) {
JavaPlugin.log(e);
}
}
public void run(IMarker[] markers, IProgressMonitor monitor) {
if (markers.length == 1) {
run(markers[0]);
return;
}
IProgressMonitor pm= monitor;
if (pm == null)
pm= new NullProgressMonitor();
try {
if (fProposal instanceof FixCorrectionProposal) {
ICleanUp cleanUp= ((FixCorrectionProposal)fProposal).getCleanUp();
if (cleanUp != null) {
Hashtable/*<ICompilationUnit, List<IProblemLocation>*/ problemLocations= new Hashtable();
for (int i= 0; i < markers.length; i++) {
IMarker marker= markers[i];
ICompilationUnit cu= getCompilationUnit(marker);
if (cu != null) {
try {
IEditorInput input= EditorUtility.getEditorInput(cu);
IProblemLocation location= findProblemLocation(input, marker);
if (location != null) {
if (!problemLocations.containsKey(cu.getPrimary())) {
problemLocations.put(cu.getPrimary(), new ArrayList());
}
List l= (List)problemLocations.get(cu.getPrimary());
l.add(location);
}
} catch (JavaModelException e) {
JavaPlugin.log(e);
}
}
}
if (problemLocations.size() > 0) {
Set cus= problemLocations.keySet();
Hashtable projects= new Hashtable();
for (Iterator iter= cus.iterator(); iter.hasNext();) {
ICompilationUnit cu= (ICompilationUnit)iter.next();
IJavaProject project= cu.getJavaProject();
if (!projects.containsKey(project)) {
projects.put(project, new ArrayList());
}
((List)projects.get(project)).add(cu);
}
pm.beginTask("", problemLocations.size() * 2 + 2 + projects.keySet().size()); //$NON-NLS-1$
String name= ""; //$NON-NLS-1$
String[] descriptions= cleanUp.getDescriptions();
if (descriptions != null && descriptions.length == 1) {
name= descriptions[0];
}
CompositeChange allChanges= new CompositeChange(name);
for (Iterator projectIter= projects.keySet().iterator(); projectIter.hasNext();) {
IJavaProject project= (IJavaProject)projectIter.next();
List compilationUnitsList= (List)projects.get(project);
ICompilationUnit[] compilationUnits= (ICompilationUnit[])compilationUnitsList.toArray(new ICompilationUnit[compilationUnitsList.size()]);
try {
cleanUpProject(project, compilationUnits, cleanUp, problemLocations, allChanges, pm);
} catch (CoreException e) {
JavaPlugin.log(e);
} finally {
pm.worked(1);
}
}
if (pm.isCanceled())
return;
allChanges.initializeValidationData(new SubProgressMonitor(pm, 1));
if (!validChanges(allChanges))
return;
PerformChangeOperation op= new PerformChangeOperation(allChanges);
op.setUndoManager(RefactoringCore.getUndoManager(), allChanges.getName());
try {
op.run(new SubProgressMonitor(pm, 1));
} catch (CoreException e1) {
JavaPlugin.log(e1);
} finally {
pm.worked(1);
}
IEditorPart part= EditorUtility.isOpenInEditor(fCompilationUnit);
if (part instanceof ITextEditor) {
((ITextEditor) part).selectAndReveal(fOffset, fLength);
part.setFocus();
}
}
}
}
} finally {
pm.done();
}
}
private boolean validChanges(CompositeChange change) {
RefactoringStatus result= new RefactoringStatus();
List files= new ArrayList();
try {
findFilesToBeModified(change, files);
} catch (JavaModelException e) {
JavaPlugin.log(e);
return false;
}
result.merge(Checks.validateModifiesFiles((IFile[])files.toArray(new IFile[files.size()]), JavaPlugin.getActiveWorkbenchShell().getShell()));
if (result.hasFatalError()) {
RefactoringStatusEntry[] entries= result.getEntries();
IStatus status;
if (entries.length > 1) {
status= new MultiStatus(JavaUI.ID_PLUGIN, 0, result.getMessageMatchingSeverity(RefactoringStatus.ERROR), null);
for (int i= 0; i < entries.length; i++) {
((MultiStatus)status).add(new Status(entries[i].getSeverity(), JavaUI.ID_PLUGIN, 0, entries[i].getMessage(), null));
}
} else {
RefactoringStatusEntry entry= entries[0];
status= new Status(entry.getSeverity(), JavaUI.ID_PLUGIN, 0, entry.getMessage(), null);
}
ErrorDialog.openError(JavaPlugin.getActiveWorkbenchShell().getShell(), CorrectionMessages.CorrectionMarkerResolutionGenerator__multiFixErrorDialog_Titel, CorrectionMessages.CorrectionMarkerResolutionGenerator_multiFixErrorDialog_description, status);
return false;
}
return true;
}
private void findFilesToBeModified(CompositeChange change, List result) throws JavaModelException {
Change[] children= change.getChildren();
for (int i=0;i < children.length;i++) {
Change child= children[i];
if (child instanceof CompositeChange) {
findFilesToBeModified((CompositeChange)child, result);
} else if (child instanceof MultiStateCompilationUnitChange) {
result.add(((MultiStateCompilationUnitChange)child).getCompilationUnit().getCorrespondingResource());
} else if (child instanceof CompilationUnitChange) {
result.add(((CompilationUnitChange)child).getCompilationUnit().getCorrespondingResource());
}
}
}
private void cleanUpProject(IJavaProject project, ICompilationUnit[] compilationUnits, ICleanUp cleanUp, Hashtable problemLocations, CompositeChange result, IProgressMonitor monitor) throws CoreException {
cleanUp.beginCleanUp(project, compilationUnits, new SubProgressMonitor(monitor, 1));
for (int i= 0; i < compilationUnits.length; i++) {
ICompilationUnit cu= compilationUnits[i];
CompilationUnit root= getASTRoot(cu, new SubProgressMonitor(monitor, 1));
List locationList= (List)problemLocations.get(cu);
IProblemLocation[] locations= (IProblemLocation[])locationList.toArray(new IProblemLocation[locationList.size()]);
IFix fix= cleanUp.createFix(root, locations);
if (monitor.isCanceled())
return;
if (fix != null) {
TextChange change= fix.createChange();
if (monitor.isCanceled())
return;
result.add(change);
monitor.worked(1);
}
}
cleanUp.endCleanUp();
}
/* (non-Javadoc)
* @see org.eclipse.ui.IMarkerResolution2#getDescription()
*/
public String getDescription() {
return fProposal.getAdditionalProposalInfo();
}
/* (non-Javadoc)
* @see org.eclipse.ui.IMarkerResolution2#getImage()
*/
public Image getImage() {
return fProposal.getImage();
}
/**
* {@inheritDoc}
*/
public IMarker[] findOtherMarkers(IMarker[] markers) {
if (!(fProposal instanceof FixCorrectionProposal))
return NO_MARKERS;
FixCorrectionProposal fix= (FixCorrectionProposal)fProposal;
final ICleanUp cleanUp= fix.getCleanUp();
if (cleanUp == null)
return NO_MARKERS;
final Hashtable fileMarkerTable= getMarkersForFiles(markers);
if (fileMarkerTable.isEmpty())
return NO_MARKERS;
Hashtable projectICUTable= getCompilationUnitsForProjects(fileMarkerTable);
if (projectICUTable.size() == 0)
return NO_MARKERS;
final List result= new ArrayList();
for (Iterator iter= projectICUTable.keySet().iterator(); iter.hasNext();) {
IJavaProject project= (IJavaProject)iter.next();
List cus= (List)projectICUTable.get(project);
ASTParser parser= getParser(project);
int start= 0;
int end= 0;
while (end < cus.size()) {
end= Math.min(start + BATCH_SIZE, cus.size());
List toParse= cus.subList(start, end);
ICompilationUnit[] units= (ICompilationUnit[])toParse.toArray(new ICompilationUnit[toParse.size()]);
parser.createASTs(units, new String[0], new ASTRequestor() {
/**
* {@inheritDoc}
*/
public void acceptAST(ICompilationUnit cu, CompilationUnit root) {
try {
IEditorInput input= EditorUtility.getEditorInput(cu);
List fileMarkers= (List)fileMarkerTable.get(cu.getResource());
for (Iterator iterator= fileMarkers.iterator(); iterator.hasNext();) {
IMarker marker= (IMarker)iterator.next();
IProblemLocation location= findProblemLocation(input, marker);
if (location != null) {
if (cleanUp.canFix(root, location)) {
result.add(marker);
}
}
}
} catch (CoreException e) {
JavaPlugin.log(e);
}
}
}, new NullProgressMonitor());
start= end;
}
}
if (result.size() == 0)
return NO_MARKERS;
return (IMarker[])result.toArray(new IMarker[result.size()]);
}
/**
* Returns the markers with the same type as fMarker.getType for each IFile.
*/
private Hashtable/*<IFile, List<IMarker>>*/ getMarkersForFiles(IMarker[] markers) {
final Hashtable result= new Hashtable();
String markerType;
try {
markerType= fMarker.getType();
} catch (CoreException e1) {
JavaPlugin.log(e1);
return result;
}
for (int i= 0; i < markers.length; i++) {
IMarker marker= markers[i];
if (!marker.equals(fMarker)) {
String currMarkerType= null;
try {
currMarkerType= marker.getType();
} catch (CoreException e1) {
JavaPlugin.log(e1);
}
if (currMarkerType != null && currMarkerType.equals(markerType)) {
IResource res= marker.getResource();
if (res instanceof IFile && res.isAccessible()) {
List markerList= (List)result.get(res);
if (markerList == null) {
markerList= new ArrayList();
result.put(res, markerList);
}
markerList.add(marker);
}
}
}
}
return result;
}
/**
* Returns the ICompilationUnits for each IJavaProject
*/
private Hashtable/*<IJavaProject, List<ICompilationUnit>>*/ getCompilationUnitsForProjects(final Hashtable/*<IFile, List<IMarker>>*/ fileMarkerTable) {
Hashtable result= new Hashtable();
for (Iterator iter= fileMarkerTable.keySet().iterator(); iter.hasNext();) {
IFile res= (IFile)iter.next();
IJavaElement element= JavaCore.create(res);
if (element instanceof ICompilationUnit) {
ICompilationUnit cu= (ICompilationUnit)element;
List cus= (List)result.get(cu.getJavaProject());
if (cus == null) {
cus= new ArrayList();
result.put(cu.getJavaProject(), cus);
}
cus.add(cu);
}
}
return result;
}
private static ASTParser getParser(IJavaProject javaProject) {
ASTParser parser= ASTParser.newParser(ASTProvider.SHARED_AST_LEVEL);
parser.setResolveBindings(true);
parser.setProject(javaProject);
return parser;
}
private static CompilationUnit getASTRoot(ICompilationUnit compilationUnit, IProgressMonitor monitor) {
CompilationUnit result= ASTProvider.getASTProvider().getAST(compilationUnit, ASTProvider.WAIT_YES, monitor);
if (result == null) {
// see bug 63554
result= ASTResolving.createQuickFixAST(compilationUnit, monitor);
}
return result;
}
}
private static final IMarkerResolution[] NO_RESOLUTIONS= new IMarkerResolution[0];
/**
* Constructor for CorrectionMarkerResolutionGenerator.
*/
public CorrectionMarkerResolutionGenerator() {
super();
}
/* (non-Javadoc)
* @see org.eclipse.ui.IMarkerResolutionGenerator2#hasResolutions(org.eclipse.core.resources.IMarker)
*/
public boolean hasResolutions(IMarker marker) {
return internalHasResolutions(marker);
}
/* (non-Javadoc)
* @see IMarkerResolutionGenerator#getResolutions(IMarker)
*/
public IMarkerResolution[] getResolutions(IMarker marker) {
return internalGetResolutions(marker);
}
private static boolean internalHasResolutions(IMarker marker) {
int id= marker.getAttribute(IJavaModelMarker.ID, -1);
ICompilationUnit cu= getCompilationUnit(marker);
return cu != null && JavaCorrectionProcessor.hasCorrections(cu, id, MarkerUtilities.getMarkerType(marker));
}
private static IMarkerResolution[] internalGetResolutions(IMarker marker) {
if (!internalHasResolutions(marker)) {
return NO_RESOLUTIONS;
}
try {
ICompilationUnit cu= getCompilationUnit(marker);
if (cu != null) {
IEditorInput input= EditorUtility.getEditorInput(cu);
if (input != null) {
IProblemLocation location= findProblemLocation(input, marker);
if (location != null) {
IInvocationContext context= new AssistContext(cu, location.getOffset(), location.getLength());
if (!hasProblem (context.getASTRoot().getProblems(), location))
return NO_RESOLUTIONS;
ArrayList proposals= new ArrayList();
JavaCorrectionProcessor.collectCorrections(context, new IProblemLocation[] { location }, proposals);
Collections.sort(proposals, new CompletionProposalComparator());
int nProposals= proposals.size();
IMarkerResolution[] resolutions= new IMarkerResolution[nProposals];
for (int i= 0; i < nProposals; i++) {
resolutions[i]= new CorrectionMarkerResolution(context.getCompilationUnit(), location.getOffset(), location.getLength(), (IJavaCompletionProposal) proposals.get(i), marker);
}
return resolutions;
}
}
}
} catch (JavaModelException e) {
JavaPlugin.log(e);
}
return NO_RESOLUTIONS;
}
private static boolean hasProblem(IProblem[] problems, IProblemLocation location) {
for (int i= 0; i < problems.length; i++) {
IProblem problem= problems[i];
if (problem.getID() == location.getProblemId() && problem.getSourceStart() == location.getOffset())
return true;
}
return false;
}
private static ICompilationUnit getCompilationUnit(IMarker marker) {
IResource res= marker.getResource();
if (res instanceof IFile && res.isAccessible()) {
IJavaElement element= JavaCore.create((IFile) res);
if (element instanceof ICompilationUnit)
return (ICompilationUnit) element;
}
return null;
}
private static IProblemLocation findProblemLocation(IEditorInput input, IMarker marker) {
IAnnotationModel model= JavaPlugin.getDefault().getCompilationUnitDocumentProvider().getAnnotationModel(input);
if (model != null) { // open in editor
Iterator iter= model.getAnnotationIterator();
while (iter.hasNext()) {
Object curr= iter.next();
if (curr instanceof JavaMarkerAnnotation) {
JavaMarkerAnnotation annot= (JavaMarkerAnnotation) curr;
if (marker.equals(annot.getMarker())) {
Position pos= model.getPosition(annot);
if (pos != null) {
return new ProblemLocation(pos.getOffset(), pos.getLength(), annot);
}
}
}
}
} else { // not open in editor
ICompilationUnit cu= getCompilationUnit(marker);
return createFromMarker(marker, cu);
}
return null;
}
private static IProblemLocation createFromMarker(IMarker marker, ICompilationUnit cu) {
try {
int id= marker.getAttribute(IJavaModelMarker.ID, -1);
int start= marker.getAttribute(IMarker.CHAR_START, -1);
int end= marker.getAttribute(IMarker.CHAR_END, -1);
int severity= marker.getAttribute(IMarker.SEVERITY, IMarker.SEVERITY_INFO);
String[] arguments= CorrectionEngine.getProblemArguments(marker);
String markerType= marker.getType();
if (cu != null && id != -1 && start != -1 && end != -1 && arguments != null) {
boolean isError= (severity == IMarker.SEVERITY_ERROR);
return new ProblemLocation(start, end - start, id, arguments, isError, markerType);
}
} catch (CoreException e) {
JavaPlugin.log(e);
}
return null;
}
}