Merge branch 'alias'
[idea/community.git] / plugins / groovy / src / org / jetbrains / plugins / groovy / lang / psi / util / GrStringUtil.java
1 package org.jetbrains.plugins.groovy.lang.psi.util;
2
3 import com.intellij.lang.ASTNode;
4 import com.intellij.openapi.util.Comparing;
5 import com.intellij.psi.PsiElement;
6 import com.intellij.psi.impl.source.tree.LeafPsiElement;
7 import org.jetbrains.annotations.NotNull;
8 import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElementFactory;
9 import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrStatement;
10 import org.jetbrains.plugins.groovy.lang.psi.api.statements.blocks.GrClosableBlock;
11 import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrExpression;
12 import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrReferenceExpression;
13 import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.literals.GrLiteral;
14 import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.literals.GrString;
15 import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.literals.GrStringInjection;
16
17 /**
18  * @author Maxim.Medvedev
19  */
20 public class GrStringUtil {
21   private static final String TRIPLE_QUOTES = "'''";
22   private static final String QUOTE = "'";
23   private static final String DOUBLE_QUOTES = "\"";
24   private static final String TRIPLE_DOUBLE_QUOTES = "\"\"\"";
25   public static final String GROOVY_LANG_GSTRING = "groovy.lang.GString";
26
27   private GrStringUtil() {
28   }
29
30   public static String escapeSymbolsForGString(String s, boolean escapeDoubleQuotes) {
31     StringBuilder b = new StringBuilder();
32     final char[] chars = s.toCharArray();
33     final int len = chars.length - 1;
34     int i;
35     for (i = 0; i < len; i++) {
36       if (chars[i] == '\\') {
37         final char next = chars[i + 1];
38         if (next == '\'') {
39           b.append('\'');
40           i++;
41         }
42         else if (next == 'n') {
43           b.append('\n');
44           i++;
45         }
46         else if (escapeDoubleQuotes && next == '"') {
47           b.append('"');
48           i++;
49         }
50         else {
51           b.append(chars[i]);
52           i++;
53           b.append(chars[i]);
54         }
55         continue;
56       }
57       if (chars[i] == '"' || chars[i] == '$') b.append('\\');
58       b.append(chars[i]);
59     }
60     if (i == len) {
61       if (chars[i] == '"') b.append('\\');
62       b.append(chars[i]);
63     }
64     return b.toString();
65   }
66
67   public static String escapeSymbolsForString(String s, boolean escapeQuotes) {
68     StringBuilder b = new StringBuilder();
69     final char[] chars = s.toCharArray();
70     final int len = chars.length - 1;
71     int i;
72     for (i = 0; i < len; i++) {
73       if (chars[i] == '\\') {
74         final char next = chars[i + 1];
75         if (next == '"' || next == '$') {
76           b.append(next);
77         }
78         else if (next == 'n') {
79           b.append('\n');
80         }
81         else if (escapeQuotes && next == '\'') {
82           b.append(next);
83         }
84         else {
85           b.append('\\');
86           b.append(next);
87         }
88         i++;
89         continue;
90       }
91       if (chars[i] == '\'') b.append('\\');
92       b.append(chars[i]);
93     }
94
95     if (i == len) {
96       if (chars[i] == '\'') b.append('\\');
97       b.append(chars[i]);
98     }
99     return b.toString();
100
101   }
102
103   public static String removeQuotes(@NotNull String s) {
104     if (s.startsWith(TRIPLE_QUOTES) || s.startsWith(TRIPLE_DOUBLE_QUOTES)) {
105       if (s.endsWith(s.substring(0, 3))) {
106         return s.substring(3, s.length() - 3);
107       }
108       else {
109         return s.substring(3);
110       }
111     }
112     else if (s.startsWith(QUOTE) || s.startsWith(DOUBLE_QUOTES)) {
113       if (s.length() >= 2 && s.endsWith(s.substring(0, 1))) {
114         return s.substring(1, s.length() - 1);
115       }
116       else {
117         return s.substring(1);
118       }
119     }
120     return s;
121   }
122
123   public static String addQuotes(String s, boolean forGString) {
124     if (forGString) {
125       if (s.contains("\n")) {
126         return TRIPLE_DOUBLE_QUOTES + s + TRIPLE_DOUBLE_QUOTES;
127       }
128       else {
129         return DOUBLE_QUOTES + s + DOUBLE_QUOTES;
130       }
131     }
132     else {
133       if (s.contains("\n")) {
134         return TRIPLE_QUOTES + s + TRIPLE_QUOTES;
135       }
136       else {
137         return QUOTE + s + QUOTE;
138       }
139     }
140   }
141
142   public static boolean isReplacedExpressionInGStringInjection(GrExpression replacedExpression) {
143     PsiElement parent = replacedExpression.getParent();
144     if (parent instanceof GrClosableBlock) {
145       parent = parent.getParent();
146     }
147     return parent instanceof GrStringInjection;
148   }
149
150   /**
151    * @param injection - expected that injection must have GrString parent
152    * @param literal
153    * @return
154    */
155   //todo use this code
156   public static GrString replaceStringInjectionByLiteral(PsiElement injection, GrLiteral literal) {
157     if (injection.getParent() instanceof GrClosableBlock) {
158       injection = injection.getParent();
159     }
160     injection = injection.getParent();
161     GrString grString = (GrString)injection.getParent();
162
163     int injectionNumber = -1;
164     for (PsiElement child : grString.getChildren()) {
165       injectionNumber++;
166       if (child == injection) {
167         break;
168       }
169     }
170     if (injectionNumber == -1) return grString;
171
172     GrString grStringWithBraces = addAllBracesInGString(grString);
173 //    injection = grStringWithBraces.getChildren()[injectionNumber];
174
175     if (literal instanceof GrString) {
176       literal = addAllBracesInGString((GrString)literal);
177     }
178
179     String literalText = escapeSymbolsForGString(removeQuotes(literal.getText()), false);
180     if(literalText.contains("\n")) literalText=escapeSymbolsForGString(literalText, true);
181
182     final GroovyPsiElementFactory factory = GroovyPsiElementFactory.getInstance(grString.getProject());
183     final GrExpression expression = factory.createExpressionFromText("\"${}" + literalText + "\"");
184
185     expression.getFirstChild().delete();
186     expression.getFirstChild().delete();
187     expression.getLastChild().delete();
188
189     final ASTNode node = grString.getNode();
190     if (expression.getFirstChild() != null) {
191       if (expression.getFirstChild() == expression.getLastChild()) {
192         node.replaceChild(injection.getNode(), expression.getFirstChild().getNode());
193       }
194       else {
195         node.addChildren(expression.getFirstChild().getNode(), expression.getLastChild().getNode(), injection.getNode());
196         injection.delete();
197       }
198     }
199     return grString;
200   }
201
202   public static GrString addAllBracesInGString(GrString grString) {
203     StringBuilder newString = new StringBuilder();
204
205     for (PsiElement child = grString.getFirstChild(); child != null; child = child.getNextSibling()) {
206       if (child instanceof GrStringInjection && ((GrStringInjection)child).getReferenceExpression() != null) {
207         final GrReferenceExpression refExpr = ((GrStringInjection)child).getReferenceExpression();
208         newString.append("${").append(refExpr != null ? refExpr.getText() : "").append('}');
209       }
210       else {
211         newString.append(child.getText());
212       }
213     }
214     return (GrString)GroovyPsiElementFactory.getInstance(grString.getProject()).createExpressionFromText(newString.toString());
215   }
216
217   public static boolean checkGStringInjectionForUnnecessaryBraces(PsiElement element) {
218     if (!(element instanceof GrStringInjection)) return false;
219     GrStringInjection injection = (GrStringInjection)element;
220     final GrClosableBlock block = injection.getClosableBlock();
221     if (block == null) return false;
222
223     final GrStatement[] statements = block.getStatements();
224     if (statements.length != 1) return false;
225
226     if (!(statements[0] instanceof GrReferenceExpression)) return false;
227
228     final PsiElement next = injection.getNextSibling();
229     if (!(next instanceof LeafPsiElement)) return false;
230
231     char nextChar = next.getText().charAt(0);
232     if (nextChar == '"' || nextChar == '$') {
233       return true;
234     }
235     final GroovyPsiElementFactory elementFactory = GroovyPsiElementFactory.getInstance(element.getProject());
236     final GrExpression gString;
237     try {
238       gString = elementFactory.createExpressionFromText("\"$" + statements[0].getText() + nextChar + '"');
239     }
240     catch (Exception e) {
241       return false;
242     }
243     if (!(gString instanceof GrString)) return false;
244
245     final PsiElement child = gString.getChildren()[0];
246     if (!(child instanceof GrStringInjection)) return false;
247
248     final PsiElement refExprCopy = ((GrStringInjection)child).getReferenceExpression();
249     if (!(refExprCopy instanceof GrReferenceExpression)) return false;
250
251     final GrReferenceExpression refExpr = (GrReferenceExpression)statements[0];
252     return Comparing.equal(refExpr.getName(), ((GrReferenceExpression)refExprCopy).getName());
253   }
254
255   public static void removeUnnecessaryBracesInGString(GrString grString) {
256     for (PsiElement child : grString.getChildren()) {
257       if (checkGStringInjectionForUnnecessaryBraces(child)) {
258         final GrClosableBlock closableBlock = ((GrStringInjection)child).getClosableBlock();
259         final GrReferenceExpression refExpr = (GrReferenceExpression)closableBlock.getStatements()[0];
260         final GrReferenceExpression copy = (GrReferenceExpression)refExpr.copy();
261         final ASTNode oldNode = closableBlock.getNode();
262         oldNode.getTreeParent().replaceChild(oldNode, copy.getNode());
263       }
264     }
265   }
266
267   public static boolean isPlainString(@NotNull GrLiteral literal) {
268     return literal.getText().startsWith("'");
269   }
270 }