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
;
40 public class ConcatenationToMessageFormatAction
implements IntentionAction
{
42 public String
getFamilyName() {
43 return CodeInsightBundle
.message("intention.replace.concatenation.with.formatted.output.family");
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
);
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
);
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();
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());
125 else if (wasLiteral
) {
126 appendArgument(args
, Collections
.singletonList(expression
), formatString
);
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());
138 else if (wasLiteral
) {
139 appendArgument(args
, Collections
.singletonList(expression
), formatString
);
142 argsToCombine
.add(expression
);
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
));
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
);
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
);
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;
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() {
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
;
235 operand
= concatenation
.getROperand();
236 if (operand
instanceof PsiLiteralExpression
) {
237 literalExpression
= (PsiLiteralExpression
)operand
;
240 return literalExpression
;