• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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} */
ConvertSwitchQuickFixProcessor()52     public ConvertSwitchQuickFixProcessor() {
53     }
54 
55     @Override
hasCorrections(ICompilationUnit cu, int problemId)56     public boolean hasCorrections(ICompilationUnit cu, int problemId) {
57         return problemId == IProblem.NonConstantExpression;
58     }
59 
60     @Override
getCorrections(IInvocationContext context, IProblemLocation[] location)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 
MigrateProposal(String expression)182         private MigrateProposal(String expression) {
183             mExpression = expression;
184         }
185 
186         @Override
apply(IDocument document)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
getSelection(IDocument document)194         public Point getSelection(IDocument document) {
195             return null;
196         }
197 
198         @Override
getAdditionalProposalInfo()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
getDisplayString()205         public String getDisplayString() {
206             return "Migrate Android Code";
207         }
208 
209         @Override
getImage()210         public Image getImage() {
211             return AdtPlugin.getAndroidLogo();
212         }
213 
214         @Override
getContextInformation()215         public IContextInformation getContextInformation() {
216             return null;
217         }
218 
219         @Override
getRelevance()220         public int getRelevance() {
221             return 50;
222         }
223     }
224 }
225