blob: 08c9fcc3fc9ee9972c3791e34d0145b475f045ec [file] [log] [blame]
/*
* Copyright (C) 2014 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.android.inspections.lint;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.fileEditor.FileDocumentManager;
import com.intellij.openapi.util.TextRange;
import com.intellij.psi.PsiElement;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* Generic lint quickfix which replaces text somewhere in the range from [startElement,endElement] by matching
* a regular expression and replacing the first group with a specified value. The regular expression can be null
* in which case the entire text range is replaced (this is used where lint's error range corresponds exactly to
* the portion we want to replace.)
*/
class ReplaceStringQuickFix implements AndroidLintQuickFix {
private final String myName;
private final String myRegexp;
private final String myNewValue;
/**
* Creates a new lint quickfix which can replace string contents at the given PSI element
*
* @param name the quickfix description, which is optional (if not specified, it will be Replace with X)
* @param regexp the regular expression
* @param newValue
*/
ReplaceStringQuickFix(@Nullable String name, @Nullable String regexp, @NotNull String newValue) {
myName = name;
myNewValue = newValue;
if (regexp != null && regexp.indexOf('(') == -1) {
regexp = "(" + Pattern.quote(regexp) + ")";
}
myRegexp = regexp;
}
@NotNull
@Override
public String getName() {
if (myName == null) {
return "Replace with " + myNewValue;
}
return myName;
}
@Nullable
protected String getNewValue() {
return myNewValue;
}
protected void editBefore(@SuppressWarnings("UnusedParameters") @NotNull Document document) {
}
protected void editAfter(@SuppressWarnings("UnusedParameters") @NotNull Document document) {
}
@Override
public void apply(@NotNull PsiElement startElement, @NotNull PsiElement endElement, @NotNull AndroidQuickfixContexts.Context context) {
Document document = FileDocumentManager.getInstance().getDocument(startElement.getContainingFile().getVirtualFile());
String newValue = getNewValue();
if (document != null && newValue != null) {
editBefore(document);
TextRange range = getRange(startElement, endElement);
if (range != null) {
document.replaceString(range.getStartOffset(), range.getEndOffset(), newValue);
editAfter(document);
}
}
}
@Nullable
private TextRange getRange(PsiElement startElement, PsiElement endElement) {
if (!startElement.isValid() || !endElement.isValid()) {
return null;
}
int start = startElement.getTextOffset();
int end = endElement.getTextOffset() + endElement.getTextLength();
if (myRegexp != null) {
try {
Pattern pattern = Pattern.compile(myRegexp, Pattern.MULTILINE);
String text = startElement.getContainingFile().getText();
String sequence = text.substring(start, end);
Matcher matcher = pattern.matcher(sequence);
if (matcher.find()) {
end = start + matcher.end(1);
start += matcher.start(1);
}
else {
return null;
}
} catch (Exception e) {
// Invalid regexp
Logger.getInstance(ReplaceStringQuickFix.class).warn("Invalid regular expression " + myRegexp);
return null;
}
}
return new TextRange(start, end);
}
@Override
public boolean isApplicable(@NotNull PsiElement startElement,
@NotNull PsiElement endElement,
@NotNull AndroidQuickfixContexts.ContextType contextType) {
return getRange(startElement, endElement) != null;
}
}