001    /*
002     * Copyright 2010-2013 JetBrains s.r.o.
003     *
004     * Licensed under the Apache License, Version 2.0 (the "License");
005     * you may not use this file except in compliance with the License.
006     * You may obtain a copy of the License at
007     *
008     * http://www.apache.org/licenses/LICENSE-2.0
009     *
010     * Unless required by applicable law or agreed to in writing, software
011     * distributed under the License is distributed on an "AS IS" BASIS,
012     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013     * See the License for the specific language governing permissions and
014     * limitations under the License.
015     */
016    
017    package org.jetbrains.jet.lang.psi;
018    
019    import com.intellij.lang.ASTNode;
020    import com.intellij.openapi.project.Project;
021    import com.intellij.openapi.util.Pair;
022    import com.intellij.psi.PsiElement;
023    import com.intellij.psi.PsiFileFactory;
024    import com.intellij.psi.util.PsiTreeUtil;
025    import com.intellij.util.LocalTimeCounter;
026    import org.jetbrains.annotations.NotNull;
027    import org.jetbrains.annotations.Nullable;
028    import org.jetbrains.jet.lang.resolve.ImportPath;
029    import org.jetbrains.jet.lang.resolve.name.Name;
030    import org.jetbrains.jet.lexer.JetKeywordToken;
031    import org.jetbrains.jet.lexer.JetTokens;
032    import org.jetbrains.jet.plugin.JetFileType;
033    
034    import java.util.Collections;
035    import java.util.List;
036    
037    public class JetPsiFactory {
038    
039        public static ASTNode createValNode(Project project) {
040            JetProperty property = createProperty(project, "val x = 1");
041            return property.getValOrVarNode();
042        }
043    
044        public static ASTNode createVarNode(Project project) {
045            JetProperty property = createProperty(project, "var x = 1");
046            return property.getValOrVarNode();
047        }
048    
049        public static ASTNode createValOrVarNode(Project project, String text) {
050            return createParameterList(project, "(" + text + " int x)").getParameters().get(0).getValOrVarNode();
051        }
052    
053        public static JetExpression createExpression(Project project, String text) {
054            JetProperty property = createProperty(project, "val x = " + text);
055            return property.getInitializer();
056        }
057    
058        public static JetValueArgumentList createCallArguments(Project project, String text) {
059            JetProperty property = createProperty(project, "val x = foo" + text);
060            JetExpression initializer = property.getInitializer();
061            JetCallExpression callExpression = (JetCallExpression) initializer;
062            return callExpression.getValueArgumentList();
063        }
064    
065        public static JetTypeArgumentList createTypeArguments(Project project, String text) {
066            JetProperty property = createProperty(project, "val x = foo" + text + "()");
067            JetExpression initializer = property.getInitializer();
068            JetCallExpression callExpression = (JetCallExpression) initializer;
069            return callExpression.getTypeArgumentList();
070        }
071    
072        public static JetTypeReference createType(Project project, String type) {
073            JetProperty property = createProperty(project, "val x : " + type);
074            return property.getTypeRef();
075        }
076    
077        @NotNull
078        public static PsiElement createStar(Project project) {
079            PsiElement star = createType(project, "List<*>").findElementAt(5);
080            assert star != null;
081            return star;
082        }
083    
084        @NotNull
085        public static PsiElement createComma(Project project) {
086            PsiElement comma = createType(project, "T<X, Y>").findElementAt(3);
087            assert comma != null;
088            return comma;
089        }
090    
091        //the pair contains the first and the last elements of a range
092        public static Pair<PsiElement, PsiElement> createColonAndWhiteSpaces(Project project) {
093            JetProperty property = createProperty(project, "val x : Int");
094            return Pair.create(property.findElementAt(5), property.findElementAt(7));
095        }
096    
097        //the pair contains the first and the last elements of a range
098        public static Pair<PsiElement, PsiElement> createTypeWhiteSpaceAndColon(Project project, String type) {
099            JetProperty property = createProperty(project, "val x: " + type);
100            return Pair.create(property.findElementAt(5), (PsiElement) property.getTypeRef());
101        }
102    
103        public static ASTNode createColonNode(Project project) {
104            JetProperty property = createProperty(project, "val x: Int");
105            return property.getNode().findChildByType(JetTokens.COLON);
106        }
107    
108        @NotNull
109        public static PsiElement createSemicolon(Project project) {
110            JetProperty property = createProperty(project, "val x: Int;");
111            PsiElement semicolon = property.findElementAt(10);
112            assert semicolon != null;
113            return semicolon;
114        }
115    
116        //the pair contains the first and the last elements of a range
117        @NotNull
118        public static Pair<PsiElement, PsiElement> createWhitespaceAndArrow(Project project) {
119            JetFunctionType functionType = (JetFunctionType) createType(project, "() -> Int").getTypeElement();
120            assert functionType != null;
121            return Pair.create(functionType.findElementAt(2), functionType.findElementAt(3));
122        }
123    
124        public static PsiElement createWhiteSpace(Project project) {
125            return createWhiteSpace(project, " ");
126        }
127    
128        private static PsiElement createWhiteSpace(Project project, String text) {
129            JetProperty property = createProperty(project, "val" + text + "x");
130            return property.findElementAt(3);
131        }
132    
133        public static PsiElement createNewLine(Project project) {
134            return createWhiteSpace(project, "\n");
135        }
136    
137        public static JetClass createClass(Project project, String text) {
138            return createDeclaration(project, text, JetClass.class);
139        }
140    
141        @NotNull
142        public static JetFile createFile(Project project, String text) {
143            return createFile(project, "dummy.jet", text);
144        }
145    
146        @NotNull
147        public static JetFile createFile(Project project, String fileName, String text) {
148            return (JetFile) PsiFileFactory.getInstance(project).createFileFromText(fileName, JetFileType.INSTANCE, text,
149                                                                                    LocalTimeCounter.currentTime(), false);
150        }
151    
152        @NotNull
153        public static JetFile createPhysicalFile(Project project, String fileName, String text) {
154            return (JetFile) PsiFileFactory.getInstance(project).createFileFromText(fileName, JetFileType.INSTANCE, text,
155                                                                                    LocalTimeCounter.currentTime(), true);
156        }
157    
158        public static JetProperty createProperty(Project project, String name, String type, boolean isVar, @Nullable String initializer) {
159            String text = (isVar ? "var " : "val ") + name + (type != null ? ":" + type : "") + (initializer == null ? "" : " = " + initializer);
160            return createProperty(project, text);
161        }
162    
163        public static JetProperty createProperty(Project project, String name, String type, boolean isVar) {
164            return createProperty(project, name, type, isVar, null);
165        }
166    
167        public static JetProperty createProperty(Project project, String text) {
168            return createDeclaration(project, text, JetProperty.class);
169        }
170    
171        public static <T> T createDeclaration(Project project, String text, Class<T> clazz) {
172            JetFile file = createFile(project, text);
173            List<JetDeclaration> dcls = file.getDeclarations();
174            assert dcls.size() == 1 : dcls.size() + " declarations in " + text;
175            @SuppressWarnings("unchecked")
176            T result = (T) dcls.get(0);
177            return result;
178        }
179    
180        public static PsiElement createNameIdentifier(Project project, String name) {
181            return createProperty(project, name, null, false).getNameIdentifier();
182        }
183    
184        public static JetSimpleNameExpression createSimpleName(Project project, String name) {
185            return (JetSimpleNameExpression) createProperty(project, name, null, false, name).getInitializer();
186        }
187    
188        public static PsiElement createIdentifier(Project project, String name) {
189            return createSimpleName(project, name).getIdentifier();
190        }
191    
192        public static JetNamedFunction createFunction(Project project, String funDecl) {
193            return createDeclaration(project, funDecl, JetNamedFunction.class);
194        }
195    
196        public static JetModifierList createModifierList(Project project, JetKeywordToken modifier) {
197            String text = modifier.getValue() + " val x";
198            JetProperty property = createProperty(project, text);
199            return property.getModifierList();
200        }
201    
202        public static JetModifierList createConstructorModifierList(Project project, JetKeywordToken modifier) {
203            JetClass aClass = createClass(project, "class C " + modifier.getValue() + " (){}");
204            return aClass.getPrimaryConstructorModifierList();
205        }
206    
207        public static JetExpression createEmptyBody(Project project) {
208            JetNamedFunction function = createFunction(project, "fun foo() {}");
209            return function.getBodyExpression();
210        }
211    
212        public static JetClassBody createEmptyClassBody(Project project) {
213            JetClass aClass = createClass(project, "class A(){}");
214            return aClass.getBody();
215        }
216    
217        public static JetParameter createParameter(Project project, String name, String type) {
218            JetNamedFunction function = createFunction(project, "fun foo(" + name + " : " + type + ") {}");
219            return function.getValueParameters().get(0);
220        }
221    
222        public static JetParameterList createParameterList(Project project, String text) {
223            JetNamedFunction function = createFunction(project, "fun foo" + text + "{}");
224            return function.getValueParameterList();
225        }
226    
227        @NotNull
228        public static JetWhenEntry createWhenEntry(@NotNull Project project, @NotNull String entryText) {
229            JetNamedFunction function = createFunction(project, "fun foo() { when(12) { " + entryText + " } }");
230            JetWhenEntry whenEntry = PsiTreeUtil.findChildOfType(function, JetWhenEntry.class);
231    
232            assert whenEntry != null : "Couldn't generate when entry";
233            assert entryText.equals(whenEntry.getText()) : "Generate when entry text differs from the given text";
234    
235            return whenEntry;
236        }
237    
238        public static JetStringTemplateEntryWithExpression createBlockStringTemplateEntry(@NotNull Project project, @NotNull JetExpression expression) {
239            JetStringTemplateExpression stringTemplateExpression = (JetStringTemplateExpression) createExpression(project,
240                                                                                                     "\"${" + expression.getText() + "}\"");
241            return (JetStringTemplateEntryWithExpression) stringTemplateExpression.getEntries()[0];
242        }
243    
244        @NotNull
245        public static JetImportDirective createImportDirective(Project project, @NotNull String path) {
246            return createImportDirective(project, new ImportPath(path));
247        }
248    
249        @NotNull
250        public static JetImportDirective createImportDirective(Project project, @NotNull ImportPath importPath) {
251            if (importPath.fqnPart().isRoot()) {
252                throw new IllegalArgumentException("import path must not be empty");
253            }
254    
255            StringBuilder importDirectiveBuilder = new StringBuilder("import ");
256            importDirectiveBuilder.append(importPath.getPathStr());
257    
258            Name alias = importPath.getAlias();
259            if (alias != null) {
260                importDirectiveBuilder.append(" as ").append(alias.asString());
261            }
262    
263            JetFile namespace = createFile(project, importDirectiveBuilder.toString());
264            return namespace.getImportDirectives().iterator().next();
265        }
266    
267        public static PsiElement createPrimaryConstructor(Project project) {
268            JetClass aClass = createClass(project, "class A()");
269            return aClass.findElementAt(7).getParent();
270        }
271    
272        public static JetSimpleNameExpression createClassLabel(Project project, @NotNull String labelName) {
273            JetThisExpression expression = (JetThisExpression) createExpression(project, "this@" + labelName);
274            return expression.getTargetLabel();
275        }
276    
277        public static JetExpression createFieldIdentifier(Project project, @NotNull String fieldName) {
278            return createExpression(project, "$" + fieldName);
279        }
280    
281        @NotNull
282        public static JetBinaryExpression createBinaryExpression(Project project, @NotNull String lhs, @NotNull String op, @NotNull String rhs) {
283            return (JetBinaryExpression) createExpression(project, lhs + " " + op + " " + rhs);
284        }
285    
286        @NotNull
287        public static JetBinaryExpression createBinaryExpression(Project project, @Nullable JetExpression lhs, @NotNull String op, @Nullable JetExpression rhs) {
288            return createBinaryExpression(project, JetPsiUtil.getText(lhs), op, JetPsiUtil.getText(rhs));
289        }
290    
291        public static JetTypeCodeFragment createTypeCodeFragment(Project project, String text, PsiElement context) {
292            return new JetTypeCodeFragmentImpl(project, "fragment.kt", text, context);
293        }
294    
295        public static JetExpressionCodeFragment createExpressionCodeFragment(Project project, String text, PsiElement context) {
296            return new JetExpressionCodeFragmentImpl(project, "fragment.kt", text, context);
297        }
298    
299        @NotNull
300        public static JetReturnExpression createReturn(Project project, @NotNull String text) {
301            return (JetReturnExpression) createExpression(project, "return " + text);
302        }
303    
304        @NotNull
305        public static JetReturnExpression createReturn(Project project, @Nullable JetExpression expression) {
306            return createReturn(project, JetPsiUtil.getText(expression));
307        }
308    
309        @NotNull
310        public static JetIfExpression createIf(Project project,
311                @Nullable JetExpression condition, @Nullable JetExpression thenExpr, @Nullable JetExpression elseExpr) {
312            return (JetIfExpression) createExpression(project, JetPsiUnparsingUtils.toIf(condition, thenExpr, elseExpr));
313        }
314    
315        @NotNull
316        public static JetValueArgument createArgumentWithName(
317                @NotNull Project project,
318                @NotNull String name,
319                @NotNull JetExpression argumentExpression
320        ) {
321            return createCallArguments(project, "(" + name + " = " + argumentExpression.getText() + ")").getArguments().get(0);
322        }
323    
324        public static class IfChainBuilder {
325            private final StringBuilder sb = new StringBuilder();
326            private boolean first = true;
327            private boolean frozen = false;
328    
329            public IfChainBuilder() {
330            }
331    
332            @NotNull
333            public IfChainBuilder ifBranch(@NotNull String conditionText, @NotNull String expressionText) {
334                if (first) {
335                    first = false;
336                } else {
337                    sb.append("else ");
338                }
339    
340                sb.append("if (").append(conditionText).append(") ").append(expressionText).append("\n");
341                return this;
342            }
343    
344            @NotNull
345            public IfChainBuilder ifBranch(@NotNull JetExpression condition, @NotNull JetExpression expression) {
346                return ifBranch(condition.getText(), expression.getText());
347            }
348    
349            @NotNull
350            public IfChainBuilder elseBranch(@NotNull String expressionText) {
351                sb.append("else ").append(expressionText);
352                return this;
353            }
354    
355            @NotNull
356            public IfChainBuilder elseBranch(@Nullable JetExpression expression) {
357                return elseBranch(JetPsiUtil.getText(expression));
358            }
359    
360            @NotNull
361            public JetIfExpression toExpression(Project project) {
362                if (!frozen) {
363                    frozen = true;
364                }
365                return (JetIfExpression) createExpression(project, sb.toString());
366            }
367        }
368    
369        public static class WhenBuilder {
370            private final StringBuilder sb = new StringBuilder("when ");
371            private boolean frozen = false;
372            private boolean inCondition = false;
373    
374            public WhenBuilder() {
375                this((String)null);
376            }
377    
378            public WhenBuilder(@Nullable String subjectText) {
379                if (subjectText != null) {
380                    sb.append("(").append(subjectText).append(") ");
381                }
382                sb.append("{\n");
383            }
384    
385            public WhenBuilder(@Nullable JetExpression subject) {
386                this(subject != null ? subject.getText() : null);
387            }
388    
389            @NotNull
390            public WhenBuilder condition(@NotNull String text) {
391                assert !frozen;
392    
393                if (!inCondition) {
394                    inCondition = true;
395                } else {
396                    sb.append(", ");
397                }
398                sb.append(text);
399    
400                return this;
401            }
402    
403            @NotNull
404            public WhenBuilder condition(@Nullable JetExpression expression) {
405                return condition(JetPsiUtil.getText(expression));
406            }
407    
408            @NotNull
409            public WhenBuilder pattern(@NotNull String typeReferenceText, boolean negated) {
410                return condition((negated ? "!is" : "is") + " " + typeReferenceText);
411            }
412    
413            @NotNull
414            public WhenBuilder pattern(@Nullable JetTypeReference typeReference, boolean negated) {
415                return pattern(JetPsiUtil.getText(typeReference), negated);
416            }
417    
418            @NotNull
419            public WhenBuilder range(@NotNull String argumentText, boolean negated) {
420                return condition((negated ? "!in" : "in") + " " + argumentText);
421            }
422    
423            @NotNull
424            public WhenBuilder range(@Nullable JetExpression argument, boolean negated) {
425                return range(JetPsiUtil.getText(argument), negated);
426            }
427    
428            @NotNull
429            public WhenBuilder branchExpression(@NotNull String expressionText) {
430                assert !frozen;
431                assert inCondition;
432    
433                inCondition = false;
434                sb.append(" -> ").append(expressionText).append("\n");
435    
436                return this;
437            }
438    
439            @NotNull
440            public WhenBuilder branchExpression(@Nullable JetExpression expression) {
441                return branchExpression(JetPsiUtil.getText(expression));
442            }
443    
444            @NotNull
445            public WhenBuilder entry(@NotNull String entryText) {
446                assert !frozen;
447                assert !inCondition;
448    
449                sb.append(entryText).append("\n");
450    
451                return this;
452            }
453    
454            @NotNull
455            public WhenBuilder entry(@Nullable JetWhenEntry whenEntry) {
456                return entry(JetPsiUtil.getText(whenEntry));
457            }
458    
459            @NotNull
460            public WhenBuilder elseEntry(@NotNull String text) {
461                return entry("else -> " + text);
462            }
463    
464            @NotNull
465            public WhenBuilder elseEntry(@Nullable JetExpression expression) {
466                return elseEntry(JetPsiUtil.getText(expression));
467            }
468    
469            @NotNull
470            public JetWhenExpression toExpression(Project project) {
471                if (!frozen) {
472                    sb.append("}");
473                    frozen = true;
474                }
475                return (JetWhenExpression) createExpression(project, sb.toString());
476            }
477        }
478    
479        public static JetExpression createFunctionBody(Project project, @NotNull String bodyText) {
480            JetFunction func = createFunction(project, "fun foo() {\n" + bodyText + "\n}");
481            return func.getBodyExpression();
482        }
483    
484        public static JetClassObject createEmptyClassObject(Project project) {
485            JetClass klass = createClass(project, "class foo { class object { } }");
486            return klass.getClassObject();
487        }
488    
489        public static JetBlockExpression wrapInABlock(@NotNull final JetExpression expression) {
490            if (expression instanceof JetBlockExpression) {
491                return (JetBlockExpression) expression;
492            }
493            JetNamedFunction function = createFunction(expression.getProject(), "fun f() { " + expression.getText() + "}");
494            JetBlockExpression block = (JetBlockExpression) function.getBodyExpression();
495            assert block != null;
496            return new JetBlockExpression(block.getNode()) {
497                @NotNull
498                @Override
499                public List<JetElement> getStatements() {
500                    return Collections.<JetElement>singletonList(expression);
501                }
502            };
503        }
504    }