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 package com.android.ide.eclipse.adt.internal.lint;
17 
18 
19 import com.android.annotations.NonNull;
20 import com.android.annotations.Nullable;
21 import com.android.ide.eclipse.adt.AdtPlugin;
22 import com.android.tools.lint.checks.AccessibilityDetector;
23 import com.android.tools.lint.checks.DetectMissingPrefix;
24 import com.android.tools.lint.checks.DosLineEndingDetector;
25 import com.android.tools.lint.checks.HardcodedValuesDetector;
26 import com.android.tools.lint.checks.InefficientWeightDetector;
27 import com.android.tools.lint.checks.ManifestDetector;
28 import com.android.tools.lint.checks.MissingIdDetector;
29 import com.android.tools.lint.checks.ObsoleteLayoutParamsDetector;
30 import com.android.tools.lint.checks.PxUsageDetector;
31 import com.android.tools.lint.checks.ScrollViewChildDetector;
32 import com.android.tools.lint.checks.SecurityDetector;
33 import com.android.tools.lint.checks.TextFieldDetector;
34 import com.android.tools.lint.checks.TranslationDetector;
35 import com.android.tools.lint.checks.TypoDetector;
36 import com.android.tools.lint.checks.TypographyDetector;
37 import com.android.tools.lint.checks.UseCompoundDrawableDetector;
38 import com.android.tools.lint.checks.UselessViewDetector;
39 import com.android.tools.lint.detector.api.Issue;
40 import com.android.tools.lint.detector.api.TextFormat;
41 
42 import org.eclipse.core.resources.IMarker;
43 import org.eclipse.core.runtime.CoreException;
44 import org.eclipse.jface.text.IDocument;
45 import org.eclipse.jface.text.contentassist.ICompletionProposal;
46 import org.eclipse.jface.text.contentassist.IContextInformation;
47 import org.eclipse.swt.graphics.Image;
48 import org.eclipse.swt.graphics.Point;
49 import org.eclipse.ui.ISharedImages;
50 import org.eclipse.ui.PartInitException;
51 import org.eclipse.ui.PlatformUI;
52 
53 import java.lang.reflect.Constructor;
54 import java.util.Collections;
55 import java.util.HashMap;
56 import java.util.List;
57 import java.util.Map;
58 
59 abstract class LintFix implements ICompletionProposal {
60     protected final IMarker mMarker;
61     protected final String mId;
62 
LintFix(String id, IMarker marker)63     protected LintFix(String id, IMarker marker) {
64         mId = id;
65         mMarker = marker;
66     }
67 
68     /**
69      * Returns true if this fix needs focus (which means that when the fix is
70      * performed from for example a {@link LintListDialog}'s Fix button) the
71      * editor needs to be given focus.
72      *
73      * @return true if this fix needs focus after being applied
74      */
needsFocus()75     public boolean needsFocus() {
76         return true;
77     }
78 
79     /**
80      * Returns true if this fix can be performed along side other fixes
81      *
82      * @return true if this fix can be performed in a bulk operation with other
83      *         fixes
84      */
isBulkCapable()85     public boolean isBulkCapable() {
86         return false;
87     }
88 
89     /**
90      * Returns true if this fix can be cancelled once it's invoked. This is the case
91      * for fixes which shows a confirmation dialog (such as the Extract String etc).
92      * This will be used to determine whether the marker can be deleted immediately
93      * (for non-cancelable fixes) or if it should be left alone and detected fix
94      * on the next save.
95      *
96      * @return true if the fix can be cancelled
97      */
isCancelable()98     public boolean isCancelable() {
99         return true;
100     }
101 
102     // ---- Implements ICompletionProposal ----
103 
104     @Override
getDisplayString()105     public String getDisplayString() {
106         return null;
107     }
108 
109     @Override
getAdditionalProposalInfo()110     public String getAdditionalProposalInfo() {
111         Issue issue = EclipseLintClient.getRegistry().getIssue(mId);
112         if (issue != null) {
113             return issue.getExplanation(TextFormat.HTML);
114         }
115 
116         return null;
117     }
118 
deleteMarker()119     public void deleteMarker() {
120         try {
121             mMarker.delete();
122         } catch (PartInitException e) {
123             AdtPlugin.log(e, null);
124         } catch (CoreException e) {
125             AdtPlugin.log(e, null);
126         }
127     }
128 
129     @Override
getSelection(IDocument document)130     public Point getSelection(IDocument document) {
131         return null;
132     }
133 
134     @Override
getImage()135     public Image getImage() {
136         ISharedImages sharedImages = PlatformUI.getWorkbench().getSharedImages();
137         return sharedImages.getImage(ISharedImages.IMG_OBJS_WARN_TSK);
138     }
139 
140     @Override
getContextInformation()141     public IContextInformation getContextInformation() {
142         return null;
143     }
144 
145     // --- Access to available fixes ---
146 
147     private static final Map<String, Class<? extends LintFix>> sFixes =
148             new HashMap<String, Class<? extends LintFix>>();
149     // Keep this map in sync with BuiltinIssueRegistry's hasAutoFix() data
150     static {
InefficientWeightDetector.INEFFICIENT_WEIGHT.getId()151         sFixes.put(InefficientWeightDetector.INEFFICIENT_WEIGHT.getId(),
152                 LinearLayoutWeightFix.class);
AccessibilityDetector.ISSUE.getId()153         sFixes.put(AccessibilityDetector.ISSUE.getId(), SetAttributeFix.class);
InefficientWeightDetector.BASELINE_WEIGHTS.getId()154         sFixes.put(InefficientWeightDetector.BASELINE_WEIGHTS.getId(), SetAttributeFix.class);
ManifestDetector.ALLOW_BACKUP.getId()155         sFixes.put(ManifestDetector.ALLOW_BACKUP.getId(), SetAttributeFix.class);
MissingIdDetector.ISSUE.getId()156         sFixes.put(MissingIdDetector.ISSUE.getId(), SetAttributeFix.class);
HardcodedValuesDetector.ISSUE.getId()157         sFixes.put(HardcodedValuesDetector.ISSUE.getId(), ExtractStringFix.class);
UselessViewDetector.USELESS_LEAF.getId()158         sFixes.put(UselessViewDetector.USELESS_LEAF.getId(), RemoveUselessViewFix.class);
UselessViewDetector.USELESS_PARENT.getId()159         sFixes.put(UselessViewDetector.USELESS_PARENT.getId(), RemoveUselessViewFix.class);
PxUsageDetector.PX_ISSUE.getId()160         sFixes.put(PxUsageDetector.PX_ISSUE.getId(), ConvertToDpFix.class);
TextFieldDetector.ISSUE.getId()161         sFixes.put(TextFieldDetector.ISSUE.getId(), SetAttributeFix.class);
SecurityDetector.EXPORTED_SERVICE.getId()162         sFixes.put(SecurityDetector.EXPORTED_SERVICE.getId(), SetAttributeFix.class);
TranslationDetector.MISSING.getId()163         sFixes.put(TranslationDetector.MISSING.getId(), SetAttributeFix.class);
DetectMissingPrefix.MISSING_NAMESPACE.getId()164         sFixes.put(DetectMissingPrefix.MISSING_NAMESPACE.getId(), AddPrefixFix.class);
ScrollViewChildDetector.ISSUE.getId()165         sFixes.put(ScrollViewChildDetector.ISSUE.getId(), SetScrollViewSizeFix.class);
ObsoleteLayoutParamsDetector.ISSUE.getId()166         sFixes.put(ObsoleteLayoutParamsDetector.ISSUE.getId(), ObsoleteLayoutParamsFix.class);
TypographyDetector.DASHES.getId()167         sFixes.put(TypographyDetector.DASHES.getId(), TypographyFix.class);
TypographyDetector.ELLIPSIS.getId()168         sFixes.put(TypographyDetector.ELLIPSIS.getId(), TypographyFix.class);
TypographyDetector.FRACTIONS.getId()169         sFixes.put(TypographyDetector.FRACTIONS.getId(), TypographyFix.class);
TypographyDetector.OTHER.getId()170         sFixes.put(TypographyDetector.OTHER.getId(), TypographyFix.class);
TypographyDetector.QUOTES.getId()171         sFixes.put(TypographyDetector.QUOTES.getId(), TypographyFix.class);
UseCompoundDrawableDetector.ISSUE.getId()172         sFixes.put(UseCompoundDrawableDetector.ISSUE.getId(),
173                 UseCompoundDrawableDetectorFix.class);
TypoDetector.ISSUE.getId()174         sFixes.put(TypoDetector.ISSUE.getId(), TypoFix.class);
DosLineEndingDetector.ISSUE.getId()175         sFixes.put(DosLineEndingDetector.ISSUE.getId(), DosLineEndingsFix.class);
176         // ApiDetector.UNSUPPORTED is provided as a marker resolution rather than
177         // a quick assistant (the marker resolution adds a suitable @TargetApi annotation)
178     }
179 
hasFix(String id)180     public static boolean hasFix(String id) {
181         return sFixes.containsKey(id);
182     }
183 
184     /**
185      * Returns one or more fixes for the given issue, or null if no fixes are available
186      *
187      * @param id the id o the issue to obtain a fix for (see {@link Issue#getId()})
188      * @param marker the marker corresponding to the error
189      * @return a nonempty list of fix, or null
190      */
191     @Nullable
getFixes(@onNull String id, @NonNull IMarker marker)192     public static List<LintFix> getFixes(@NonNull String id, @NonNull IMarker marker) {
193         Class<? extends LintFix> clazz = sFixes.get(id);
194         if (clazz != null) {
195             try {
196                 Constructor<? extends LintFix> constructor = clazz.getDeclaredConstructor(
197                         String.class, IMarker.class);
198                 constructor.setAccessible(true);
199                 LintFix fix = constructor.newInstance(id, marker);
200                 List<LintFix> alternatives = fix.getAllFixes();
201                 if (alternatives != null) {
202                     return alternatives;
203                 } else {
204                     return Collections.singletonList(fix);
205                 }
206             } catch (Throwable t) {
207                 AdtPlugin.log(t, null);
208             }
209         }
210 
211         return null;
212     }
213 
214     /**
215      * Returns a full list of fixes for this issue. This will produce a list of
216      * multiple fixes, in the desired order, which provide alternative ways of
217      * fixing the issue.
218      *
219      * @return a list of fixes to fix this issue, or null if there are no
220      *         variations
221      */
222     @Nullable
getAllFixes()223     protected List<LintFix> getAllFixes() {
224         return null;
225     }
226 }
227