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}