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