diff options
Diffstat (limited to 'eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/ConvertSwitchQuickFixProcessor.java')
-rw-r--r-- | eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/ConvertSwitchQuickFixProcessor.java | 224 |
1 files changed, 224 insertions, 0 deletions
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/ConvertSwitchQuickFixProcessor.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/ConvertSwitchQuickFixProcessor.java new file mode 100644 index 000000000..a99dc7601 --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/ConvertSwitchQuickFixProcessor.java @@ -0,0 +1,224 @@ +/* + * 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; + } + } +} |