/* * Copyright 2000-2014 JetBrains s.r.o. * * 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. */ /** * @author cdr */ package com.intellij.codeInspection.i18n; import com.intellij.codeInsight.CodeInsightBundle; import com.intellij.codeInsight.FileModificationService; import com.intellij.codeInspection.LocalQuickFix; import com.intellij.codeInspection.ProblemDescriptor; import com.intellij.lang.properties.psi.PropertiesFile; import com.intellij.lang.properties.psi.PropertyCreationHandler; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.command.CommandProcessor; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.editor.SelectionModel; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.Messages; import com.intellij.openapi.util.TextRange; import com.intellij.openapi.util.text.StringUtil; import com.intellij.psi.*; import com.intellij.psi.codeStyle.CodeStyleManager; import com.intellij.psi.codeStyle.JavaCodeStyleManager; import com.intellij.psi.util.PsiUtilBase; import com.intellij.util.IncorrectOperationException; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.Collection; public class I18nizeQuickFix implements LocalQuickFix, I18nQuickFixHandler { private static final Logger LOG = Logger.getInstance("#com.intellij.codeInsight.i18n.I18nizeQuickFix"); private TextRange mySelectionRange; @Override public final void applyFix(@NotNull final Project project, @NotNull final ProblemDescriptor descriptor) { doFix(descriptor, project); } @Override public boolean startInWriteAction() { return false; } @Override @NotNull public String getFamilyName() { return CodeInsightBundle.message("inspection.i18n.quickfix"); } @Override public void checkApplicability(final PsiFile psiFile, final Editor editor) throws IncorrectOperationException { PsiLiteralExpression literalExpression = I18nizeAction.getEnclosingStringLiteral(psiFile, editor); if (literalExpression != null) { SelectionModel selectionModel = editor.getSelectionModel(); if (!selectionModel.hasSelection()) return; int start = selectionModel.getSelectionStart(); int end = selectionModel.getSelectionEnd(); TextRange textRange = literalExpression.getTextRange(); if (textRange.contains(start) && textRange.contains(end)) { mySelectionRange = new TextRange(start, end); return; } } String message = CodeInsightBundle.message("i18nize.error.message"); throw new IncorrectOperationException(message); } @Override public void performI18nization(final PsiFile psiFile, final Editor editor, PsiLiteralExpression literalExpression, Collection<PropertiesFile> propertiesFiles, String key, String value, String i18nizedText, PsiExpression[] parameters, final PropertyCreationHandler propertyCreationHandler) throws IncorrectOperationException { Project project = psiFile.getProject(); propertyCreationHandler.createProperty(project, propertiesFiles, key, value, parameters); try { final PsiElement newExpression = doReplacementInJava(psiFile, editor,literalExpression, i18nizedText); reformatAndCorrectReferences(newExpression); } catch (IncorrectOperationException e) { Messages.showErrorDialog(project, CodeInsightBundle.message("inspection.i18n.expression.is.invalid.error.message"), CodeInsightBundle.message("inspection.error.dialog.title")); } } @Override public JavaI18nizeQuickFixDialog createDialog(Project project, Editor editor, PsiFile psiFile) { final PsiLiteralExpression literalExpression = I18nizeAction.getEnclosingStringLiteral(psiFile, editor); return createDialog(project, psiFile, literalExpression); } private void doFix(final ProblemDescriptor descriptor, final Project project) { final PsiLiteralExpression literalExpression = (PsiLiteralExpression)descriptor.getPsiElement(); final PsiFile psiFile = literalExpression.getContainingFile(); if (!JavaI18nizeQuickFixDialog.isAvailable(psiFile)) { return; } final JavaI18nizeQuickFixDialog dialog = createDialog(project, psiFile, literalExpression); if (!dialog.showAndGet()) { return; } final Collection<PropertiesFile> propertiesFiles = dialog.getAllPropertiesFiles(); if (!FileModificationService.getInstance().preparePsiElementForWrite(literalExpression)) return; for (PropertiesFile file : propertiesFiles) { if (file.findPropertyByKey(dialog.getKey()) == null && !FileModificationService.getInstance().prepareFileForWrite(file.getContainingFile())) return; } CommandProcessor.getInstance().executeCommand(project, () -> ApplicationManager.getApplication().runWriteAction(() -> { try { performI18nization(psiFile, PsiUtilBase.findEditor(psiFile), dialog.getLiteralExpression(), propertiesFiles, dialog.getKey(), dialog.getValue(), dialog.getI18nizedText(), dialog.getParameters(), dialog.getPropertyCreationHandler()); } catch (IncorrectOperationException e) { LOG.error(e); } }), CodeInsightBundle.message("quickfix.i18n.command.name"), project); } protected PsiElement doReplacementInJava(@NotNull final PsiFile psiFile, final Editor editor, final PsiLiteralExpression literalExpression, String i18nizedText) throws IncorrectOperationException { return replaceStringLiteral(literalExpression, i18nizedText); } private static void reformatAndCorrectReferences(PsiElement newExpression) throws IncorrectOperationException { final Project project = newExpression.getProject(); newExpression = JavaCodeStyleManager.getInstance(project).shortenClassReferences(newExpression); CodeStyleManager.getInstance(project).reformat(newExpression); } protected JavaI18nizeQuickFixDialog createDialog(final Project project, final PsiFile context, final PsiLiteralExpression literalExpression) { String value = (String)literalExpression.getValue(); if (mySelectionRange != null) { TextRange literalRange = literalExpression.getTextRange(); TextRange intersection = literalRange.intersection(mySelectionRange); value = literalExpression.getText().substring(intersection.getStartOffset() - literalRange.getStartOffset(), intersection.getEndOffset() - literalRange.getStartOffset()); } value = StringUtil.escapeStringCharacters(value); return new JavaI18nizeQuickFixDialog(project, context, literalExpression, value, null, true, true); } @Nullable private static PsiBinaryExpression breakStringLiteral(PsiLiteralExpression literalExpression, int offset) throws IncorrectOperationException { TextRange literalRange = literalExpression.getTextRange(); PsiElementFactory factory = JavaPsiFacade.getInstance(literalExpression.getProject()).getElementFactory(); if (literalRange.getStartOffset()+1 < offset && offset < literalRange.getEndOffset()-1) { PsiBinaryExpression expression = (PsiBinaryExpression)factory.createExpressionFromText("a + b", literalExpression); String value = (String)literalExpression.getValue(); int breakIndex = offset - literalRange.getStartOffset()-1; String lsubstring = value.substring(0, breakIndex); expression.getLOperand().replace(factory.createExpressionFromText("\""+lsubstring+"\"", literalExpression)); String rsubstring = value.substring(breakIndex); expression.getROperand().replace(factory.createExpressionFromText("\""+rsubstring+"\"", literalExpression)); return (PsiBinaryExpression)literalExpression.replace(expression); } return null; } private PsiElement replaceStringLiteral(PsiLiteralExpression literalExpression, String i18nizedText) throws IncorrectOperationException { PsiElementFactory factory = JavaPsiFacade.getInstance(literalExpression.getProject()).getElementFactory(); if (mySelectionRange != null) { try { PsiBinaryExpression binaryExpression = breakStringLiteral(literalExpression, mySelectionRange.getEndOffset()); if (binaryExpression != null) { literalExpression = (PsiLiteralExpression)binaryExpression.getLOperand(); } binaryExpression = breakStringLiteral(literalExpression, mySelectionRange.getStartOffset()); if (binaryExpression != null) { literalExpression = (PsiLiteralExpression)binaryExpression.getROperand(); } } catch (IncorrectOperationException e) { LOG.error(e); } } PsiExpression expression = factory.createExpressionFromText(i18nizedText, literalExpression); return literalExpression.replace(expression); } }