| /* |
| * 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; |
| } |
| } |
| } |