Home | History | Annotate | Download | only in build
      1 /*
      2  * Copyright (C) 2011 The Android Open Source Project
      3  *
      4  * Licensed under the Eclipse Public License, Version 1.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.eclipse.org/org/documents/epl-v10.php
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.ide.eclipse.adt.internal.build;
     18 
     19 import com.android.ide.eclipse.adt.AdtPlugin;
     20 import com.android.ide.eclipse.adt.AdtUtils;
     21 
     22 import org.eclipse.core.resources.IMarker;
     23 import org.eclipse.core.resources.IResource;
     24 import org.eclipse.core.runtime.CoreException;
     25 import org.eclipse.jdt.core.IBuffer;
     26 import org.eclipse.jdt.core.ICompilationUnit;
     27 import org.eclipse.jdt.core.compiler.IProblem;
     28 import org.eclipse.jdt.core.dom.ASTNode;
     29 import org.eclipse.jdt.core.dom.QualifiedName;
     30 import org.eclipse.jdt.ui.text.java.IInvocationContext;
     31 import org.eclipse.jdt.ui.text.java.IJavaCompletionProposal;
     32 import org.eclipse.jdt.ui.text.java.IProblemLocation;
     33 import org.eclipse.jdt.ui.text.java.IQuickFixProcessor;
     34 import org.eclipse.jface.text.IDocument;
     35 import org.eclipse.jface.text.contentassist.IContextInformation;
     36 import org.eclipse.swt.graphics.Image;
     37 import org.eclipse.swt.graphics.Point;
     38 import org.eclipse.swt.widgets.Shell;
     39 import org.eclipse.ui.editors.text.TextFileDocumentProvider;
     40 import org.eclipse.ui.texteditor.IDocumentProvider;
     41 
     42 import java.util.List;
     43 
     44 /**
     45  * A quickfix processor which looks for "case expressions must be constant
     46  * expressions" errors, and if they apply to fields in a class named R, it
     47  * assumes this is code related to library projects that are no longer final and
     48  * will need to be rewritten to use if-else chains instead.
     49  */
     50 public class ConvertSwitchQuickFixProcessor implements IQuickFixProcessor {
     51     /** Constructs a new {@link ConvertSwitchQuickFixProcessor} */
     52     public ConvertSwitchQuickFixProcessor() {
     53     }
     54 
     55     @Override
     56     public boolean hasCorrections(ICompilationUnit cu, int problemId) {
     57         return problemId == IProblem.NonConstantExpression;
     58     }
     59 
     60     @Override
     61     public IJavaCompletionProposal[] getCorrections(IInvocationContext context,
     62             IProblemLocation[] location) throws CoreException {
     63         if (location == null || location.length == 0) {
     64             return null;
     65         }
     66         ASTNode coveringNode = context.getCoveringNode();
     67         if (coveringNode == null) {
     68             return null;
     69         }
     70 
     71         // Look up the fully qualified name of the non-constant expression, if any, and
     72         // make sure it's R-something.
     73         if (coveringNode.getNodeType() == ASTNode.SIMPLE_NAME) {
     74             coveringNode = coveringNode.getParent();
     75             if (coveringNode == null) {
     76                 return null;
     77             }
     78         }
     79         if (coveringNode.getNodeType() != ASTNode.QUALIFIED_NAME) {
     80             return null;
     81         }
     82         QualifiedName name = (QualifiedName) coveringNode;
     83         if (!name.getFullyQualifiedName().startsWith("R.")) { //$NON-NLS-1$
     84             return null;
     85         }
     86 
     87         IProblemLocation error = location[0];
     88         int errorStart = error.getOffset();
     89         int errorLength = error.getLength();
     90         int caret = context.getSelectionOffset();
     91 
     92         // Even though the hasCorrections() method above will return false for everything
     93         // other than non-constant expression errors, it turns out this getCorrections()
     94         // method will ALSO be called on lines where there is no such error. In particular,
     95         // if you have an invalid cast expression like this:
     96         //     Button button = findViewById(R.id.textView);
     97         // then this method will be called, and the expression will pass all of the above
     98         // checks. However, we -don't- want to show a migrate code suggestion in that case!
     99         // Therefore, we'll need to check if we're *actually* on a line with the given
    100         // problem.
    101         //
    102         // Unfortunately, we don't get passed the problemId again, and there's no access
    103         // to it. So instead we'll need to look up the markers on the line, and see
    104         // if we actually have a constant expression warning. This is not pretty!!
    105 
    106         boolean foundError = false;
    107         ICompilationUnit compilationUnit = context.getCompilationUnit();
    108         IResource file = compilationUnit.getResource();
    109         if (file != null) {
    110             IDocumentProvider provider = new TextFileDocumentProvider();
    111             try {
    112                 provider.connect(file);
    113                 IDocument document = provider.getDocument(file);
    114                 if (document != null) {
    115                     List<IMarker> markers = AdtUtils.findMarkersOnLine(IMarker.PROBLEM,
    116                             file, document, errorStart);
    117                     for (IMarker marker : markers) {
    118                         String message = marker.getAttribute(IMarker.MESSAGE, "");
    119                         // There are no other attributes in the marker we can use to identify
    120                         // the exact error, so we'll need to resort to the actual message
    121                         // text even though that would not work if the messages had been
    122                         // localized... This can also break if the error messages change. Yuck.
    123                         if (message.contains("constant expressions")) { //$NON-NLS-1$
    124                             foundError = true;
    125                         }
    126                     }
    127                 }
    128             } catch (Exception e) {
    129                 AdtPlugin.log(e, "Can't validate error message in %1$s", file.getName());
    130             } finally {
    131                 provider.disconnect(file);
    132             }
    133         }
    134         if (!foundError) {
    135             // Not a constant-expression warning, so do nothing
    136             return null;
    137         }
    138 
    139         IBuffer buffer = compilationUnit.getBuffer();
    140         boolean sameLine = false;
    141         // See if the caret is on the same line as the error
    142         if (caret <= errorStart) {
    143             // Search backwards to beginning of line
    144             for (int i = errorStart; i >= 0; i--) {
    145                 if (i <= caret) {
    146                     sameLine = true;
    147                     break;
    148                 }
    149                 char c = buffer.getChar(i);
    150                 if (c == '\n') {
    151                     break;
    152                 }
    153             }
    154         } else {
    155             // Search forwards to the end of the line
    156             for (int i = errorStart + errorLength, n = buffer.getLength(); i < n; i++) {
    157                 if (i >= caret) {
    158                     sameLine = true;
    159                     break;
    160                 }
    161                 char c = buffer.getChar(i);
    162                 if (c == '\n') {
    163                     break;
    164                 }
    165             }
    166         }
    167 
    168         if (sameLine) {
    169             String expression = buffer.getText(errorStart, errorLength);
    170             return new IJavaCompletionProposal[] {
    171                 new MigrateProposal(expression)
    172             };
    173         }
    174 
    175         return null;
    176     }
    177 
    178     /** Proposal for the quick fix which displays an explanation message to the user */
    179     private class MigrateProposal implements IJavaCompletionProposal {
    180         private String mExpression;
    181 
    182         private MigrateProposal(String expression) {
    183             mExpression = expression;
    184         }
    185 
    186         @Override
    187         public void apply(IDocument document) {
    188             Shell shell = AdtPlugin.getShell();
    189             ConvertSwitchDialog dialog = new ConvertSwitchDialog(shell, mExpression);
    190             dialog.open();
    191         }
    192 
    193         @Override
    194         public Point getSelection(IDocument document) {
    195             return null;
    196         }
    197 
    198         @Override
    199         public String getAdditionalProposalInfo() {
    200             return "As of ADT 14, resource fields cannot be used as switch cases. Invoke this " +
    201                     "fix to get more information.";
    202         }
    203 
    204         @Override
    205         public String getDisplayString() {
    206             return "Migrate Android Code";
    207         }
    208 
    209         @Override
    210         public Image getImage() {
    211             return AdtPlugin.getAndroidLogo();
    212         }
    213 
    214         @Override
    215         public IContextInformation getContextInformation() {
    216             return null;
    217         }
    218 
    219         @Override
    220         public int getRelevance() {
    221             return 50;
    222         }
    223     }
    224 }
    225