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 */
16package com.android.ide.eclipse.adt.internal.lint;
17
18
19import com.android.annotations.NonNull;
20import com.android.annotations.Nullable;
21import com.android.ide.eclipse.adt.AdtPlugin;
22import com.android.tools.lint.checks.AccessibilityDetector;
23import com.android.tools.lint.checks.DetectMissingPrefix;
24import com.android.tools.lint.checks.DosLineEndingDetector;
25import com.android.tools.lint.checks.HardcodedValuesDetector;
26import com.android.tools.lint.checks.InefficientWeightDetector;
27import com.android.tools.lint.checks.ManifestDetector;
28import com.android.tools.lint.checks.MissingIdDetector;
29import com.android.tools.lint.checks.ObsoleteLayoutParamsDetector;
30import com.android.tools.lint.checks.PxUsageDetector;
31import com.android.tools.lint.checks.ScrollViewChildDetector;
32import com.android.tools.lint.checks.SecurityDetector;
33import com.android.tools.lint.checks.TextFieldDetector;
34import com.android.tools.lint.checks.TranslationDetector;
35import com.android.tools.lint.checks.TypoDetector;
36import com.android.tools.lint.checks.TypographyDetector;
37import com.android.tools.lint.checks.UseCompoundDrawableDetector;
38import com.android.tools.lint.checks.UselessViewDetector;
39import com.android.tools.lint.detector.api.Issue;
40import com.android.tools.lint.detector.api.Issue.OutputFormat;
41
42import org.eclipse.core.resources.IMarker;
43import org.eclipse.core.runtime.CoreException;
44import org.eclipse.jface.text.IDocument;
45import org.eclipse.jface.text.contentassist.ICompletionProposal;
46import org.eclipse.jface.text.contentassist.IContextInformation;
47import org.eclipse.swt.graphics.Image;
48import org.eclipse.swt.graphics.Point;
49import org.eclipse.ui.ISharedImages;
50import org.eclipse.ui.PartInitException;
51import org.eclipse.ui.PlatformUI;
52
53import java.lang.reflect.Constructor;
54import java.util.Collections;
55import java.util.HashMap;
56import java.util.List;
57import java.util.Map;
58
59abstract class LintFix implements ICompletionProposal {
60    protected final IMarker mMarker;
61    protected final String mId;
62
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     */
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     */
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     */
98    public boolean isCancelable() {
99        return true;
100    }
101
102    // ---- Implements ICompletionProposal ----
103
104    @Override
105    public String getDisplayString() {
106        return null;
107    }
108
109    @Override
110    public String getAdditionalProposalInfo() {
111        Issue issue = EclipseLintClient.getRegistry().getIssue(mId);
112        if (issue != null) {
113            return issue.getExplanation(OutputFormat.HTML);
114        }
115
116        return null;
117    }
118
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
130    public Point getSelection(IDocument document) {
131        return null;
132    }
133
134    @Override
135    public Image getImage() {
136        ISharedImages sharedImages = PlatformUI.getWorkbench().getSharedImages();
137        return sharedImages.getImage(ISharedImages.IMG_OBJS_WARN_TSK);
138    }
139
140    @Override
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 {
151        sFixes.put(InefficientWeightDetector.INEFFICIENT_WEIGHT.getId(),
152                LinearLayoutWeightFix.class);
153        sFixes.put(AccessibilityDetector.ISSUE.getId(), SetAttributeFix.class);
154        sFixes.put(InefficientWeightDetector.BASELINE_WEIGHTS.getId(), SetAttributeFix.class);
155        sFixes.put(ManifestDetector.ALLOW_BACKUP.getId(), SetAttributeFix.class);
156        sFixes.put(MissingIdDetector.ISSUE.getId(), SetAttributeFix.class);
157        sFixes.put(HardcodedValuesDetector.ISSUE.getId(), ExtractStringFix.class);
158        sFixes.put(UselessViewDetector.USELESS_LEAF.getId(), RemoveUselessViewFix.class);
159        sFixes.put(UselessViewDetector.USELESS_PARENT.getId(), RemoveUselessViewFix.class);
160        sFixes.put(PxUsageDetector.PX_ISSUE.getId(), ConvertToDpFix.class);
161        sFixes.put(TextFieldDetector.ISSUE.getId(), SetAttributeFix.class);
162        sFixes.put(SecurityDetector.EXPORTED_SERVICE.getId(), SetAttributeFix.class);
163        sFixes.put(TranslationDetector.MISSING.getId(), SetAttributeFix.class);
164        sFixes.put(DetectMissingPrefix.MISSING_NAMESPACE.getId(), AddPrefixFix.class);
165        sFixes.put(ScrollViewChildDetector.ISSUE.getId(), SetScrollViewSizeFix.class);
166        sFixes.put(ObsoleteLayoutParamsDetector.ISSUE.getId(), ObsoleteLayoutParamsFix.class);
167        sFixes.put(TypographyDetector.DASHES.getId(), TypographyFix.class);
168        sFixes.put(TypographyDetector.ELLIPSIS.getId(), TypographyFix.class);
169        sFixes.put(TypographyDetector.FRACTIONS.getId(), TypographyFix.class);
170        sFixes.put(TypographyDetector.OTHER.getId(), TypographyFix.class);
171        sFixes.put(TypographyDetector.QUOTES.getId(), TypographyFix.class);
172        sFixes.put(UseCompoundDrawableDetector.ISSUE.getId(),
173                UseCompoundDrawableDetectorFix.class);
174        sFixes.put(TypoDetector.ISSUE.getId(), TypoFix.class);
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
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
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
223    protected List<LintFix> getAllFixes() {
224        return null;
225    }
226}
227