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