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