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.
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(){
58 doFix(descriptor
, project
);
64 public String
getName() {
65 return CodeInsightBundle
.message("inspection.i18n.quickfix");
69 public String
getFamilyName() {
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
);
86 String message
= CodeInsightBundle
.message("i18nize.error.message");
87 throw new IncorrectOperationException(message
);
90 public void performI18nization(final PsiFile psiFile
,
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
);
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
)) {
120 final JavaI18nizeQuickFixDialog dialog
= createDialog(project
, psiFile
, literalExpression
);
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(){
132 ApplicationManager
.getApplication().runWriteAction(new Runnable(){
135 performI18nization(psiFile
, getEditorForFile(psiFile
), dialog
.getLiteralExpression(), propertiesFiles
, dialog
.getKey(),
136 dialog
.getValue(), dialog
.getI18nizedText(), dialog
.getParameters(),
137 dialog
.getPropertyCreationHandler());
139 catch (IncorrectOperationException 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();
165 protected PsiElement
doReplacementInJava(@NotNull final PsiFile psiFile
,
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);
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
);
207 private PsiElement
replaceStringLiteral(PsiLiteralExpression literalExpression
, String i18nizedText
) throws IncorrectOperationException
{
208 PsiElementFactory factory
= JavaPsiFacade
.getInstance(literalExpression
.getProject()).getElementFactory();
209 if (mySelectionRange
!= null) {
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
) {
225 PsiExpression expression
= factory
.createExpressionFromText(i18nizedText
, literalExpression
);
226 return literalExpression
.replace(expression
);