blob: a99dc76016cc93392e05163126c482973a66bec5 [file] [log] [blame]
/*
* Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Eclipse Public License, Version 1.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.eclipse.org/org/documents/epl-v10.php
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.ide.eclipse.adt.internal.build;
import com.android.ide.eclipse.adt.AdtPlugin;
import com.android.ide.eclipse.adt.AdtUtils;
import org.eclipse.core.resources.IMarker;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.jdt.core.IBuffer;
import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.compiler.IProblem;
import org.eclipse.jdt.core.dom.ASTNode;
import org.eclipse.jdt.core.dom.QualifiedName;
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.ui.text.java.IQuickFixProcessor;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.contentassist.IContextInformation;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.ui.editors.text.TextFileDocumentProvider;
import org.eclipse.ui.texteditor.IDocumentProvider;
import java.util.List;
/**
* A quickfix processor which looks for "case expressions must be constant
* expressions" errors, and if they apply to fields in a class named R, it
* assumes this is code related to library projects that are no longer final and
* will need to be rewritten to use if-else chains instead.
*/
public class ConvertSwitchQuickFixProcessor implements IQuickFixProcessor {
/** Constructs a new {@link ConvertSwitchQuickFixProcessor} */
public ConvertSwitchQuickFixProcessor() {
}
@Override
public boolean hasCorrections(ICompilationUnit cu, int problemId) {
return problemId == IProblem.NonConstantExpression;
}
@Override
public IJavaCompletionProposal[] getCorrections(IInvocationContext context,
IProblemLocation[] location) throws CoreException {
if (location == null || location.length == 0) {
return null;
}
ASTNode coveringNode = context.getCoveringNode();
if (coveringNode == null) {
return null;
}
// Look up the fully qualified name of the non-constant expression, if any, and
// make sure it's R-something.
if (coveringNode.getNodeType() == ASTNode.SIMPLE_NAME) {
coveringNode = coveringNode.getParent();
if (coveringNode == null) {
return null;
}
}
if (coveringNode.getNodeType() != ASTNode.QUALIFIED_NAME) {
return null;
}
QualifiedName name = (QualifiedName) coveringNode;
if (!name.getFullyQualifiedName().startsWith("R.")) { //$NON-NLS-1$
return null;
}
IProblemLocation error = location[0];
int errorStart = error.getOffset();
int errorLength = error.getLength();
int caret = context.getSelectionOffset();
// Even though the hasCorrections() method above will return false for everything
// other than non-constant expression errors, it turns out this getCorrections()
// method will ALSO be called on lines where there is no such error. In particular,
// if you have an invalid cast expression like this:
// Button button = findViewById(R.id.textView);
// then this method will be called, and the expression will pass all of the above
// checks. However, we -don't- want to show a migrate code suggestion in that case!
// Therefore, we'll need to check if we're *actually* on a line with the given
// problem.
//
// Unfortunately, we don't get passed the problemId again, and there's no access
// to it. So instead we'll need to look up the markers on the line, and see
// if we actually have a constant expression warning. This is not pretty!!
boolean foundError = false;
ICompilationUnit compilationUnit = context.getCompilationUnit();
IResource file = compilationUnit.getResource();
if (file != null) {
IDocumentProvider provider = new TextFileDocumentProvider();
try {
provider.connect(file);
IDocument document = provider.getDocument(file);
if (document != null) {
List<IMarker> markers = AdtUtils.findMarkersOnLine(IMarker.PROBLEM,
file, document, errorStart);
for (IMarker marker : markers) {
String message = marker.getAttribute(IMarker.MESSAGE, "");
// There are no other attributes in the marker we can use to identify
// the exact error, so we'll need to resort to the actual message
// text even though that would not work if the messages had been
// localized... This can also break if the error messages change. Yuck.
if (message.contains("constant expressions")) { //$NON-NLS-1$
foundError = true;
}
}
}
} catch (Exception e) {
AdtPlugin.log(e, "Can't validate error message in %1$s", file.getName());
} finally {
provider.disconnect(file);
}
}
if (!foundError) {
// Not a constant-expression warning, so do nothing
return null;
}
IBuffer buffer = compilationUnit.getBuffer();
boolean sameLine = false;
// See if the caret is on the same line as the error
if (caret <= errorStart) {
// Search backwards to beginning of line
for (int i = errorStart; i >= 0; i--) {
if (i <= caret) {
sameLine = true;
break;
}
char c = buffer.getChar(i);
if (c == '\n') {
break;
}
}
} else {
// Search forwards to the end of the line
for (int i = errorStart + errorLength, n = buffer.getLength(); i < n; i++) {
if (i >= caret) {
sameLine = true;
break;
}
char c = buffer.getChar(i);
if (c == '\n') {
break;
}
}
}
if (sameLine) {
String expression = buffer.getText(errorStart, errorLength);
return new IJavaCompletionProposal[] {
new MigrateProposal(expression)
};
}
return null;
}
/** Proposal for the quick fix which displays an explanation message to the user */
private class MigrateProposal implements IJavaCompletionProposal {
private String mExpression;
private MigrateProposal(String expression) {
mExpression = expression;
}
@Override
public void apply(IDocument document) {
Shell shell = AdtPlugin.getShell();
ConvertSwitchDialog dialog = new ConvertSwitchDialog(shell, mExpression);
dialog.open();
}
@Override
public Point getSelection(IDocument document) {
return null;
}
@Override
public String getAdditionalProposalInfo() {
return "As of ADT 14, resource fields cannot be used as switch cases. Invoke this " +
"fix to get more information.";
}
@Override
public String getDisplayString() {
return "Migrate Android Code";
}
@Override
public Image getImage() {
return AdtPlugin.getAndroidLogo();
}
@Override
public IContextInformation getContextInformation() {
return null;
}
@Override
public int getRelevance() {
return 50;
}
}
}