1 package com
.intellij
.codeInspection
.i18n
.folding
;
3 import com
.intellij
.codeInsight
.AnnotationUtil
;
4 import com
.intellij
.codeInsight
.folding
.JavaCodeFoldingSettings
;
5 import com
.intellij
.codeInspection
.i18n
.JavaI18nUtil
;
6 import com
.intellij
.lang
.ASTNode
;
7 import com
.intellij
.lang
.StdLanguages
;
8 import com
.intellij
.lang
.folding
.FoldingBuilderEx
;
9 import com
.intellij
.lang
.folding
.FoldingDescriptor
;
10 import com
.intellij
.lang
.properties
.psi
.Property
;
11 import com
.intellij
.openapi
.editor
.Document
;
12 import com
.intellij
.psi
.*;
13 import com
.intellij
.psi
.impl
.JavaConstantExpressionEvaluator
;
14 import com
.intellij
.psi
.impl
.source
.SourceTreeToPsiMap
;
15 import com
.intellij
.util
.containers
.ContainerUtil
;
16 import org
.jetbrains
.annotations
.NotNull
;
21 * @author Konstantin Bulenkov
23 public class PropertyFoldingBuilder
extends FoldingBuilderEx
{
26 public FoldingDescriptor
[] buildFoldRegions(@NotNull PsiElement element
, @NotNull Document document
, boolean quick
) {
27 if (!(element
instanceof PsiJavaFile
) || quick
|| !isFoldingsOn()) {
28 return FoldingDescriptor
.EMPTY
;
30 final PsiJavaFile file
= (PsiJavaFile
) element
;
31 final List
<FoldingDescriptor
> result
= new ArrayList
<FoldingDescriptor
>();
32 boolean hasJsp
= ContainerUtil
.intersects(Arrays
.asList(StdLanguages
.JSP
, StdLanguages
.JSPX
), file
.getViewProvider().getLanguages());
33 //hack here because JspFile PSI elements are not threaded correctly via nextSibling/prevSibling
34 file
.accept(hasJsp ?
new JavaRecursiveElementVisitor() {
36 public void visitLiteralExpression(PsiLiteralExpression expression
) {
37 checkLiteral(expression
, result
);
39 } : new JavaRecursiveElementWalkingVisitor() {
41 public void visitLiteralExpression(PsiLiteralExpression expression
) {
42 checkLiteral(expression
, result
);
46 return result
.toArray(new FoldingDescriptor
[result
.size()]);
49 private static boolean isFoldingsOn() {
50 return JavaCodeFoldingSettings
.getInstance().isCollapseI18nMessages();
53 private static void checkLiteral(PsiLiteralExpression expression
, List
<FoldingDescriptor
> result
) {
54 if (isI18nProperty(expression
)) {
55 final String msg
= getI18nMessage(expression
);
57 final PsiElement parent
= expression
.getParent();
58 if (!msg
.equals(expression
.getText()) &&
59 parent
instanceof PsiExpressionList
&&
60 ((PsiExpressionList
)parent
).getExpressions()[0] == expression
) {
61 final PsiExpressionList expressions
= (PsiExpressionList
)parent
;
62 final int count
= JavaI18nUtil
.getPropertyValueParamsMaxCount(expression
);
63 final PsiExpression
[] args
= expressions
.getExpressions();
64 if (args
.length
== 1 + count
&& parent
.getParent() instanceof PsiMethodCallExpression
) {
66 for (int i
= 1; i
< count
+ 1; i
++) {
67 Object value
= JavaConstantExpressionEvaluator
.computeConstantExpression(args
[i
], false);
69 if (!(args
[i
] instanceof PsiReferenceExpression
)) {
76 result
.add(new FoldingDescriptor(parent
.getParent(), parent
.getParent().getTextRange()));
82 result
.add(new FoldingDescriptor(expression
, expression
.getTextRange()));
87 public String
getPlaceholderText(@NotNull ASTNode node
) {
88 final PsiElement element
= SourceTreeToPsiMap
.treeElementToPsi(node
);
89 if (element
instanceof PsiLiteralExpression
) {
90 return getI18nMessage((PsiLiteralExpression
)element
);
91 } else if (element
instanceof PsiMethodCallExpression
) {
92 return formatMethodCallExpression((PsiMethodCallExpression
)element
);
94 return element
.getText();
97 private static String
formatMethodCallExpression(PsiMethodCallExpression methodCallExpression
) {
98 final PsiExpression
[] args
= methodCallExpression
.getArgumentList().getExpressions();
100 && args
[0] instanceof PsiLiteralExpression
101 && isI18nProperty((PsiLiteralExpression
)args
[0])) {
102 final int count
= JavaI18nUtil
.getPropertyValueParamsMaxCount((PsiLiteralExpression
)args
[0]);
103 if (args
.length
== 1 + count
) {
104 String text
= getI18nMessage((PsiLiteralExpression
)args
[0]);
105 for (int i
= 1; i
< count
+ 1; i
++) {
106 Object value
= JavaConstantExpressionEvaluator
.computeConstantExpression(args
[i
], false);
108 if (args
[i
] instanceof PsiReferenceExpression
) {
109 value
= "{" + args
[i
].getText() + "}";
116 text
= text
.replace("{" + (i
- 1) + "}", value
.toString());
119 if (!text
.equals(methodCallExpression
.getText())) {
120 text
= text
.replace("''", "'");
127 return methodCallExpression
.getText();
130 private static String
getI18nMessage(PsiLiteralExpression literal
) {
131 if (isI18nProperty(literal
)) {
132 final PsiReference
[] references
= literal
.getReferences();
133 for (PsiReference reference
: references
) {
134 if (reference
instanceof PsiPolyVariantReference
) {
135 final ResolveResult
[] results
= ((PsiPolyVariantReference
)reference
).multiResolve(false);
136 for (ResolveResult result
: results
) {
137 final PsiElement element
= result
.getElement();
138 if (element
instanceof Property
) {
139 return "\"" + ((Property
)element
).getValue() + "\"";
143 final PsiElement element
= reference
.resolve();
144 if (element
instanceof Property
) {
145 return "\"" + ((Property
)element
).getValue() + "\"";
150 return literal
.getText();
153 public boolean isCollapsedByDefault(@NotNull ASTNode node
) {
154 return isFoldingsOn();
158 public static boolean isI18nProperty(PsiLiteralExpression expr
) {
159 if (! isStringLiteral(expr
)) return false;
161 final Map
<String
, Object
> annotationParams
= new HashMap
<String
, Object
>();
162 annotationParams
.put(AnnotationUtil
.PROPERTY_KEY_RESOURCE_BUNDLE_PARAMETER
, null);
163 return JavaI18nUtil
.mustBePropertyKey(expr
, annotationParams
);
166 private static boolean isStringLiteral(PsiLiteralExpression expr
) {
168 if (expr
== null || (text
= expr
.getText()) == null) return false;
169 return text
.startsWith("\"") && text
.endsWith("\"") && text
.length() > 2;