Inspections - pass onTheFly into ProblemDescriptors & use it to create LAZY refs...
[fedora-idea.git] / plugins / java-i18n / src / com / intellij / codeInspection / i18n / I18nizeQuickFix.java
blob70caa236ca9b53b8e40eaebf999e799879a7885c
1 /*
2 * Copyright 2000-2009 JetBrains s.r.o.
4 * Licensed under the Apache License, Version 2.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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 /**
18 * @author cdr
20 package com.intellij.codeInspection.i18n;
22 import com.intellij.codeInsight.CodeInsightBundle;
23 import com.intellij.codeInsight.CodeInsightUtilBase;
24 import com.intellij.codeInspection.LocalQuickFix;
25 import com.intellij.codeInspection.ProblemDescriptor;
26 import com.intellij.lang.properties.psi.PropertiesFile;
27 import com.intellij.lang.properties.psi.PropertyCreationHandler;
28 import com.intellij.openapi.application.ApplicationManager;
29 import com.intellij.openapi.command.CommandProcessor;
30 import com.intellij.openapi.diagnostic.Logger;
31 import com.intellij.openapi.editor.Editor;
32 import com.intellij.openapi.editor.SelectionModel;
33 import com.intellij.openapi.fileEditor.FileEditor;
34 import com.intellij.openapi.fileEditor.FileEditorManager;
35 import com.intellij.openapi.fileEditor.TextEditor;
36 import com.intellij.openapi.project.Project;
37 import com.intellij.openapi.ui.Messages;
38 import com.intellij.openapi.util.TextRange;
39 import com.intellij.openapi.util.text.StringUtil;
40 import com.intellij.openapi.vfs.VirtualFile;
41 import com.intellij.psi.*;
42 import com.intellij.psi.codeStyle.CodeStyleManager;
43 import com.intellij.psi.codeStyle.JavaCodeStyleManager;
44 import com.intellij.util.IncorrectOperationException;
45 import org.jetbrains.annotations.NotNull;
46 import org.jetbrains.annotations.Nullable;
48 import java.util.Collection;
50 public class I18nizeQuickFix implements LocalQuickFix, I18nQuickFixHandler {
51 private static final Logger LOG = Logger.getInstance("#com.intellij.codeInsight.i18n.I18nizeQuickFix");
52 private TextRange mySelectionRange;
54 public void applyFix(@NotNull final Project project, @NotNull final ProblemDescriptor descriptor) {
55 // do it later because the fix was called inside writeAction
56 ApplicationManager.getApplication().invokeLater(new Runnable(){
57 public void run() {
58 doFix(descriptor, project);
60 });
63 @NotNull
64 public String getName() {
65 return CodeInsightBundle.message("inspection.i18n.quickfix");
68 @NotNull
69 public String getFamilyName() {
70 return getName();
73 public void checkApplicability(final PsiFile psiFile, final Editor editor) throws IncorrectOperationException {
74 PsiLiteralExpression literalExpression = I18nizeAction.getEnclosingStringLiteral(psiFile, editor);
75 if (literalExpression != null) {
76 SelectionModel selectionModel = editor.getSelectionModel();
77 if (!selectionModel.hasSelection()) return;
78 int start = selectionModel.getSelectionStart();
79 int end = selectionModel.getSelectionEnd();
80 TextRange textRange = literalExpression.getTextRange();
81 if (textRange.contains(start) && textRange.contains(end)) {
82 mySelectionRange = new TextRange(start, end);
83 return;
86 String message = CodeInsightBundle.message("i18nize.error.message");
87 throw new IncorrectOperationException(message);
90 public void performI18nization(final PsiFile psiFile,
91 final Editor editor,
92 PsiLiteralExpression literalExpression,
93 Collection<PropertiesFile> propertiesFiles,
94 String key, String value, String i18nizedText,
95 PsiExpression[] parameters,
96 final PropertyCreationHandler propertyCreationHandler) throws IncorrectOperationException {
97 Project project = psiFile.getProject();
98 propertyCreationHandler.createProperty(project, propertiesFiles, key, value, parameters);
99 try {
100 final PsiElement newExpression = doReplacementInJava(psiFile, editor,literalExpression, i18nizedText);
101 reformatAndCorrectReferences(newExpression);
103 catch (IncorrectOperationException e) {
104 Messages.showErrorDialog(project, CodeInsightBundle.message("inspection.i18n.expression.is.invalid.error.message"),
105 CodeInsightBundle.message("inspection.error.dialog.title"));
109 public JavaI18nizeQuickFixDialog createDialog(Project project, Editor editor, PsiFile psiFile) {
110 final PsiLiteralExpression literalExpression = I18nizeAction.getEnclosingStringLiteral(psiFile, editor);
111 return createDialog(project, psiFile, literalExpression);
114 private void doFix(final ProblemDescriptor descriptor, final Project project) {
115 final PsiLiteralExpression literalExpression = (PsiLiteralExpression)descriptor.getPsiElement();
116 final PsiFile psiFile = literalExpression.getContainingFile();
117 if (!JavaI18nizeQuickFixDialog.isAvailable(psiFile)) {
118 return;
120 final JavaI18nizeQuickFixDialog dialog = createDialog(project, psiFile, literalExpression);
121 dialog.show();
122 if (!dialog.isOK()) return;
123 final Collection<PropertiesFile> propertiesFiles = dialog.getAllPropertiesFiles();
125 if (!CodeInsightUtilBase.preparePsiElementForWrite(literalExpression)) return;
126 for (PropertiesFile file : propertiesFiles) {
127 if (!CodeInsightUtilBase.prepareFileForWrite(file)) return;
130 CommandProcessor.getInstance().executeCommand(project, new Runnable(){
131 public void run() {
132 ApplicationManager.getApplication().runWriteAction(new Runnable(){
133 public void run() {
134 try {
135 performI18nization(psiFile, getEditorForFile(psiFile), dialog.getLiteralExpression(), propertiesFiles, dialog.getKey(),
136 dialog.getValue(), dialog.getI18nizedText(), dialog.getParameters(),
137 dialog.getPropertyCreationHandler());
139 catch (IncorrectOperationException e) {
140 LOG.error(e);
145 }, CodeInsightBundle.message("quickfix.i18n.command.name"),project);
148 private static Editor getEditorForFile(@NotNull final PsiFile psiFile) {
149 VirtualFile virtualFile = psiFile.getVirtualFile();
150 if (virtualFile == null) {
151 PsiFile originalFile = psiFile.getOriginalFile();
152 if (originalFile == null) return null;
153 virtualFile = originalFile.getVirtualFile();
154 if (virtualFile == null) return null;
156 final FileEditor[] editors = FileEditorManager.getInstance(psiFile.getProject()).getEditors(virtualFile);
157 for (FileEditor editor : editors) {
158 if (editor instanceof TextEditor) {
159 return ((TextEditor)editor).getEditor();
162 return null;
165 protected PsiElement doReplacementInJava(@NotNull final PsiFile psiFile,
166 final Editor editor,
167 final PsiLiteralExpression literalExpression,
168 String i18nizedText) throws IncorrectOperationException {
169 return replaceStringLiteral(literalExpression, i18nizedText);
172 private static void reformatAndCorrectReferences(PsiElement newExpression) throws IncorrectOperationException {
173 final Project project = newExpression.getProject();
174 newExpression = JavaCodeStyleManager.getInstance(project).shortenClassReferences(newExpression);
175 CodeStyleManager.getInstance(project).reformat(newExpression);
178 protected JavaI18nizeQuickFixDialog createDialog(final Project project, final PsiFile context, final PsiLiteralExpression literalExpression) {
179 String value = (String)literalExpression.getValue();
180 if (mySelectionRange != null) {
181 TextRange literalRange = literalExpression.getTextRange();
182 TextRange intersection = literalRange.intersection(mySelectionRange);
183 value = literalExpression.getText().substring(intersection.getStartOffset() - literalRange.getStartOffset(), intersection.getEndOffset() - literalRange.getStartOffset());
185 value = StringUtil.escapeStringCharacters(value);
186 return new JavaI18nizeQuickFixDialog(project, context, literalExpression, value, null, true, true);
189 @Nullable
190 private static PsiBinaryExpression breakStringLiteral(PsiLiteralExpression literalExpression, int offset) throws IncorrectOperationException {
191 TextRange literalRange = literalExpression.getTextRange();
192 PsiElementFactory factory = JavaPsiFacade.getInstance(literalExpression.getProject()).getElementFactory();
193 if (literalRange.getStartOffset()+1 < offset && offset < literalRange.getEndOffset()-1) {
194 PsiBinaryExpression expression = (PsiBinaryExpression)factory.createExpressionFromText("a + b", literalExpression);
195 String value = (String)literalExpression.getValue();
196 int breakIndex = offset - literalRange.getStartOffset()-1;
197 String lsubstring = value.substring(0, breakIndex);
198 expression.getLOperand().replace(factory.createExpressionFromText("\""+lsubstring+"\"", literalExpression));
199 String rsubstring = value.substring(breakIndex);
200 expression.getROperand().replace(factory.createExpressionFromText("\""+rsubstring+"\"", literalExpression));
201 return (PsiBinaryExpression)literalExpression.replace(expression);
204 return null;
207 private PsiElement replaceStringLiteral(PsiLiteralExpression literalExpression, String i18nizedText) throws IncorrectOperationException {
208 PsiElementFactory factory = JavaPsiFacade.getInstance(literalExpression.getProject()).getElementFactory();
209 if (mySelectionRange != null) {
210 try {
211 PsiBinaryExpression binaryExpression = breakStringLiteral(literalExpression, mySelectionRange.getEndOffset());
212 if (binaryExpression != null) {
213 literalExpression = (PsiLiteralExpression)binaryExpression.getLOperand();
215 binaryExpression = breakStringLiteral(literalExpression, mySelectionRange.getStartOffset());
216 if (binaryExpression != null) {
217 literalExpression = (PsiLiteralExpression)binaryExpression.getROperand();
220 catch (IncorrectOperationException e) {
221 LOG.error(e);
225 PsiExpression expression = factory.createExpressionFromText(i18nizedText, literalExpression);
226 return literalExpression.replace(expression);