update copyright
[fedora-idea.git] / java / java-impl / src / com / intellij / codeInsight / intention / impl / ConcatenationToMessageFormatAction.java
blob7ae060197aaa7335e0659262724f58f397597b2d
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.
16 package com.intellij.codeInsight.intention.impl;
18 import com.intellij.codeInsight.CodeInsightBundle;
19 import com.intellij.codeInsight.CodeInsightUtilBase;
20 import com.intellij.codeInsight.intention.IntentionAction;
21 import com.intellij.openapi.editor.Editor;
22 import com.intellij.openapi.project.Project;
23 import com.intellij.openapi.util.text.StringUtil;
24 import com.intellij.pom.java.LanguageLevel;
25 import com.intellij.psi.*;
26 import com.intellij.psi.codeStyle.JavaCodeStyleManager;
27 import com.intellij.psi.search.GlobalSearchScope;
28 import com.intellij.psi.util.PsiTreeUtil;
29 import com.intellij.psi.util.PsiUtil;
30 import com.intellij.util.IncorrectOperationException;
31 import org.jetbrains.annotations.NotNull;
33 import java.util.ArrayList;
34 import java.util.Collections;
35 import java.util.List;
37 /**
38 * @author ven
40 public class ConcatenationToMessageFormatAction implements IntentionAction {
41 @NotNull
42 public String getFamilyName() {
43 return CodeInsightBundle.message("intention.replace.concatenation.with.formatted.output.family");
46 @NotNull
47 public String getText() {
48 return CodeInsightBundle.message("intention.replace.concatenation.with.formatted.output.text");
51 public void invoke(@NotNull Project project, Editor editor, PsiFile file) throws IncorrectOperationException {
52 if (!CodeInsightUtilBase.prepareFileForWrite(file)) return;
53 PsiBinaryExpression concatenation = getEnclosingLiteralConcatenation(file, editor);
54 PsiManager manager = concatenation.getManager();
55 StringBuffer formatString = new StringBuffer();
56 List<PsiExpression> args = new ArrayList<PsiExpression>();
57 ArrayList<PsiExpression> argsToCombine = new ArrayList<PsiExpression>();
58 calculateFormatAndArguments(concatenation, formatString, args, argsToCombine, false);
59 appendArgument(args, argsToCombine, formatString);
61 PsiMethodCallExpression call = (PsiMethodCallExpression) JavaPsiFacade.getInstance(manager.getProject()).getElementFactory().createExpressionFromText("java.text.MessageFormat.format()", concatenation);
62 PsiExpressionList argumentList = call.getArgumentList();
63 String format = prepareString(formatString.toString());
64 PsiExpression formatArgument = JavaPsiFacade.getInstance(manager.getProject()).getElementFactory().createExpressionFromText("\"" + format + "\"", null);
65 argumentList.add(formatArgument);
66 if (PsiUtil.isLanguageLevel5OrHigher(file)) {
67 for (PsiExpression arg : args) {
68 argumentList.add(arg);
71 else {
72 final PsiNewExpression arrayArg = (PsiNewExpression)JavaPsiFacade.getInstance(manager.getProject()).getElementFactory()
73 .createExpressionFromText("new java.lang.Object[]{}", null);
74 final PsiArrayInitializerExpression arrayInitializer = arrayArg.getArrayInitializer();
75 assert arrayInitializer != null;
76 for (PsiExpression arg : args) {
77 arrayInitializer.add(arg);
80 argumentList.add(arrayArg);
82 call = (PsiMethodCallExpression) JavaCodeStyleManager.getInstance(project).shortenClassReferences(call);
83 call = (PsiMethodCallExpression) manager.getCodeStyleManager().reformat(call);
84 concatenation.replace(call);
87 public static String prepareString(final String s) {
88 return repeatSingleQuotes(StringUtil.escapeStringCharacters(s));
91 private static String repeatSingleQuotes(String s) {
92 StringBuilder buffer = new StringBuilder();
93 for (int i = 0; i < s.length(); i++) {
94 final char c = s.charAt(i);
95 if (c == '\'') {
96 buffer.append(c);
97 buffer.append(c);
98 } else {
99 buffer.append(c);
102 return buffer.toString();
105 public static boolean calculateFormatAndArguments(PsiExpression expression,
106 StringBuffer formatString,
107 List<PsiExpression> args,
108 List<PsiExpression> argsToCombine,
109 boolean wasLiteral) throws IncorrectOperationException {
110 if (expression == null) return wasLiteral;
111 if (expression instanceof PsiBinaryExpression) {
112 final PsiType type = expression.getType();
113 if (type != null
114 && type.equalsToText("java.lang.String")
115 && ((PsiBinaryExpression)expression).getOperationSign().getTokenType() == JavaTokenType.PLUS) {
116 wasLiteral = calculateFormatAndArguments(((PsiBinaryExpression)expression).getLOperand(), formatString, args, argsToCombine, wasLiteral);
117 wasLiteral = calculateFormatAndArguments(((PsiBinaryExpression)expression).getROperand(), formatString, args, argsToCombine, wasLiteral);
119 else if (expression instanceof PsiLiteralExpression && ((PsiLiteralExpression)expression).getValue() instanceof String) {
120 appendArgument(args, argsToCombine, formatString);
121 argsToCombine.clear();
122 formatString.append(((PsiLiteralExpression)expression).getValue());
123 return true;
125 else if (wasLiteral) {
126 appendArgument(args, Collections.singletonList(expression), formatString);
128 else {
129 argsToCombine.add(expression);
132 else if (expression instanceof PsiLiteralExpression && ((PsiLiteralExpression)expression).getValue() instanceof String) {
133 appendArgument(args, argsToCombine, formatString);
134 argsToCombine.clear();
135 formatString.append(((PsiLiteralExpression)expression).getValue());
136 return true;
138 else if (wasLiteral) {
139 appendArgument(args, Collections.singletonList(expression), formatString);
141 else {
142 argsToCombine.add(expression);
145 return wasLiteral;
148 private static void appendArgument(List<PsiExpression> args, List<PsiExpression> argsToCombine, StringBuffer formatString) throws IncorrectOperationException {
149 if (argsToCombine.isEmpty()) return;
150 PsiExpression argument = argsToCombine.get(0);
151 final PsiManager manager = argument.getManager();
152 final PsiElementFactory factory = JavaPsiFacade.getInstance(manager.getProject()).getElementFactory();
153 for (int i = 1; i < argsToCombine.size(); i++) {
154 PsiBinaryExpression newArg = (PsiBinaryExpression) factory.createExpressionFromText("a+b", null);
155 newArg.getLOperand().replace(argument);
156 PsiExpression rOperand = newArg.getROperand();
157 assert rOperand != null;
158 rOperand.replace(argsToCombine.get(i));
159 argument = newArg;
162 formatString.append("{").append(args.size()).append("}");
163 args.add(getBoxedArgument(argument));
166 private static PsiExpression getBoxedArgument(PsiExpression arg) throws IncorrectOperationException {
167 arg = PsiUtil.deparenthesizeExpression(arg);
168 assert arg != null;
169 final PsiManager manager = arg.getManager();
170 final PsiElementFactory factory = JavaPsiFacade.getInstance(manager.getProject()).getElementFactory();
171 if (!PsiUtil.isLanguageLevel5OrHigher(arg)) {
172 final PsiType type = arg.getType();
173 if (type instanceof PsiPrimitiveType && !type.equals(PsiType.NULL)) {
174 final PsiPrimitiveType primitiveType = (PsiPrimitiveType)type;
175 final String boxedQName = primitiveType.getBoxedTypeName();
176 if (boxedQName != null) {
177 final GlobalSearchScope resolveScope = arg.getResolveScope();
178 final PsiJavaCodeReferenceElement ref = factory.createReferenceElementByFQClassName(boxedQName, resolveScope);
179 final PsiNewExpression newExpr = (PsiNewExpression)factory.createExpressionFromText("new A(b)", null);
180 final PsiElement classRef = newExpr.getClassReference();
181 assert classRef != null;
182 classRef.replace(ref);
183 final PsiExpressionList argumentList = newExpr.getArgumentList();
184 assert argumentList != null;
185 argumentList.getExpressions()[0].replace(arg);
186 return newExpr;
191 return arg;
194 public boolean isAvailable(@NotNull Project project, Editor editor, PsiFile file) {
195 return PsiUtil.getLanguageLevel(file).compareTo(LanguageLevel.JDK_1_4) >= 0 && getEnclosingLiteralConcatenation(file, editor) != null;
198 public static PsiBinaryExpression getEnclosingLiteralConcatenation(@NotNull PsiFile file, @NotNull Editor editor) {
199 final PsiElement elementAt = file.findElementAt(editor.getCaretModel().getOffset());
200 return getEnclosingLiteralConcatenation(elementAt);
203 public static PsiBinaryExpression getEnclosingLiteralConcatenation(final PsiElement psiElement) {
204 PsiBinaryExpression element = PsiTreeUtil.getParentOfType(psiElement, PsiBinaryExpression.class, false, PsiMember.class);
205 if (element == null) return null;
206 PsiBinaryExpression concatenation = null;
207 boolean stringLiteralOccured = false;
208 while (true) {
209 PsiExpression lOperand = element.getLOperand();
210 PsiExpression rOperand = element.getROperand();
211 if (element.getOperationSign().getTokenType() != JavaTokenType.PLUS) return concatenation;
212 stringLiteralOccured |= lOperand instanceof PsiLiteralExpression && ((PsiLiteralExpression)lOperand).getValue() instanceof String ||
213 rOperand instanceof PsiLiteralExpression && ((PsiLiteralExpression)rOperand).getValue() instanceof String;
215 if (stringLiteralOccured) {
216 concatenation = element;
218 PsiElement parent = element.getParent();
219 if (!(parent instanceof PsiBinaryExpression)) return concatenation;
220 element = (PsiBinaryExpression) parent;
224 public boolean startInWriteAction() {
225 return true;
228 public static PsiLiteralExpression getContainingLiteral(final PsiBinaryExpression concatenation) {
229 PsiExpression operand = concatenation.getLOperand();
230 PsiLiteralExpression literalExpression = null;
231 if (operand instanceof PsiLiteralExpression) {
232 literalExpression = (PsiLiteralExpression)operand;
234 else {
235 operand = concatenation.getROperand();
236 if (operand instanceof PsiLiteralExpression) {
237 literalExpression = (PsiLiteralExpression)operand;
240 return literalExpression;