1/*
2 * Copyright (C) 2012 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
18import com.android.ide.eclipse.adt.AdtPlugin;
19import com.android.tools.lint.checks.TypoDetector;
20
21import org.eclipse.core.resources.IMarker;
22import org.eclipse.jface.text.BadLocationException;
23import org.eclipse.jface.text.FindReplaceDocumentAdapter;
24import org.eclipse.jface.text.IDocument;
25import org.eclipse.jface.text.IRegion;
26import org.eclipse.wst.sse.core.internal.provisional.IStructuredModel;
27import org.w3c.dom.Node;
28
29import java.util.ArrayList;
30import java.util.List;
31
32/** Quickfix for fixing typos */
33@SuppressWarnings("restriction") // DOM model
34final class TypoFix extends DocumentFix {
35    private String mTypo;
36    private String mReplacement;
37
38    private TypoFix(String id, IMarker marker) {
39        super(id, marker);
40    }
41
42    @Override
43    public String getDisplayString() {
44        return String.format("Replace \"%1$s\" by \"%2$s\"", mTypo, mReplacement);
45    }
46
47    @Override
48    public boolean needsFocus() {
49        return false;
50    }
51
52    @Override
53    public boolean isCancelable() {
54        return false;
55    }
56
57    @Override
58    protected void apply(IDocument document, IStructuredModel model, Node node,
59            int start, int end) {
60        String message = mMarker.getAttribute(IMarker.MESSAGE, "");
61        String typo = TypoDetector.getTypo(message);
62        if (typo == null) {
63            return;
64        }
65        List<String> replacements = TypoDetector.getSuggestions(message);
66        if (replacements == null || replacements.isEmpty()) {
67            return;
68        }
69
70        try {
71            String current = document.get(start, end-start);
72            if (current.equals(typo)) {
73                document.replace(start, end - start, replacements.get(0));
74            } else {
75                // The buffer has been edited; try to find the typo.
76                FindReplaceDocumentAdapter finder = new FindReplaceDocumentAdapter(document);
77                IRegion forward = finder.find(start, typo, true /*forward*/, true, true, false);
78                IRegion backward = finder.find(start, typo, false /*forward*/, true, true, false);
79                if (forward != null && backward != null) {
80                    // Pick the closest one
81                    int forwardDelta = forward.getOffset() - start;
82                    int backwardDelta = start - backward.getOffset();
83                    if (forwardDelta < backwardDelta) {
84                        start = forward.getOffset();
85                    } else {
86                        start = backward.getOffset();
87                    }
88                } else if (forward != null) {
89                    start = forward.getOffset();
90                } else if (backward != null) {
91                    start = backward.getOffset();
92                } else {
93                    return;
94                }
95                end = start + typo.length();
96                document.replace(start, end - start, replacements.get(0));
97            }
98        } catch (BadLocationException e) {
99            AdtPlugin.log(e, null);
100        }
101    }
102
103    @Override
104    protected List<LintFix> getAllFixes() {
105        String message = mMarker.getAttribute(IMarker.MESSAGE, "");
106        String typo = TypoDetector.getTypo(message);
107        List<String> replacements = TypoDetector.getSuggestions(message);
108        if (!replacements.isEmpty() && typo != null) {
109            List<LintFix> allFixes = new ArrayList<LintFix>(replacements.size());
110            for (String replacement : replacements) {
111                TypoFix fix = new TypoFix(mId, mMarker);
112                fix.mTypo = typo;
113                fix.mReplacement = replacement;
114                allFixes.add(fix);
115            }
116
117            return allFixes;
118        }
119
120        return null;
121    }
122}