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.psi.PsiElement;
022 import com.intellij.psi.PsiFileFactory;
023 import com.intellij.psi.util.PsiTreeUtil;
024 import com.intellij.util.LocalTimeCounter;
025 import kotlin.KotlinPackage;
026 import kotlin.Pair;
027 import org.jetbrains.annotations.NotNull;
028 import org.jetbrains.annotations.Nullable;
029 import org.jetbrains.jet.lang.resolve.ImportPath;
030 import org.jetbrains.jet.lang.resolve.name.Name;
031 import org.jetbrains.jet.lexer.JetKeywordToken;
032 import org.jetbrains.jet.plugin.JetFileType;
033
034 import java.util.Collection;
035 import java.util.Collections;
036 import java.util.List;
037
038 public class JetPsiFactory {
039
040 @NotNull
041 public static ASTNode createValNode(Project project) {
042 JetProperty property = createProperty(project, "val x = 1");
043 return property.getValOrVarNode();
044 }
045
046 @NotNull
047 public static ASTNode createVarNode(Project project) {
048 JetProperty property = createProperty(project, "var x = 1");
049 return property.getValOrVarNode();
050 }
051
052 @NotNull
053 public static ASTNode createValOrVarNode(Project project, String text) {
054 return createParameterList(project, "(" + text + " int x)").getParameters().get(0).getValOrVarNode();
055 }
056
057 @NotNull
058 public static JetExpression createExpression(Project project, String text) {
059 JetProperty property = createProperty(project, "val x = " + text);
060 return property.getInitializer();
061 }
062
063 @NotNull
064 public static JetValueArgumentList createCallArguments(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.getValueArgumentList();
069 }
070
071 @NotNull
072 public static JetTypeArgumentList createTypeArguments(Project project, String text) {
073 JetProperty property = createProperty(project, "val x = foo" + text + "()");
074 JetExpression initializer = property.getInitializer();
075 JetCallExpression callExpression = (JetCallExpression) initializer;
076 return callExpression.getTypeArgumentList();
077 }
078
079 @NotNull
080 public static JetTypeReference createType(Project project, String type) {
081 JetProperty property = createProperty(project, "val x : " + type);
082 return property.getTypeRef();
083 }
084
085 @NotNull
086 public static PsiElement createStar(Project project) {
087 PsiElement star = createType(project, "List<*>").findElementAt(5);
088 assert star != null;
089 return star;
090 }
091
092 @NotNull
093 public static PsiElement createComma(Project project) {
094 PsiElement comma = createType(project, "T<X, Y>").findElementAt(3);
095 assert comma != null;
096 return comma;
097 }
098
099 @NotNull
100 public static PsiElement createColon(Project project) {
101 JetProperty property = createProperty(project, "val x: Int");
102 PsiElement colon = property.findElementAt(5);
103 assert colon != null;
104 return colon;
105 }
106
107 @NotNull
108 public static PsiElement createEQ(Project project) {
109 PsiElement eq = createFunction(project, "fun foo() = foo").getEqualsToken();
110 assert eq != null;
111 return eq;
112 }
113
114 @NotNull
115 public static PsiElement createSemicolon(Project project) {
116 JetProperty property = createProperty(project, "val x: Int;");
117 PsiElement semicolon = property.findElementAt(10);
118 assert semicolon != null;
119 return semicolon;
120 }
121
122 //the pair contains the first and the last elements of a range
123 @NotNull
124 public static Pair<PsiElement, PsiElement> createWhitespaceAndArrow(Project project) {
125 JetFunctionType functionType = (JetFunctionType) createType(project, "() -> Int").getTypeElement();
126 assert functionType != null;
127 return new Pair<PsiElement, PsiElement>(functionType.findElementAt(2), functionType.findElementAt(3));
128 }
129
130 @NotNull
131 public static PsiElement createWhiteSpace(Project project) {
132 return createWhiteSpace(project, " ");
133 }
134
135 @NotNull
136 public static PsiElement createWhiteSpace(Project project, String text) {
137 JetProperty property = createProperty(project, "val" + text + "x");
138 return property.findElementAt(3);
139 }
140
141 @NotNull
142 public static PsiElement createNewLine(Project project) {
143 return createWhiteSpace(project, "\n");
144 }
145
146 @NotNull
147 public static JetClass createClass(Project project, String text) {
148 return createDeclaration(project, text, JetClass.class);
149 }
150
151 @NotNull
152 public static JetFile createFile(Project project, String text) {
153 return createFile(project, "dummy.kt", text);
154 }
155
156 @NotNull
157 public static JetFile createFile(Project project, String fileName, String text) {
158 return (JetFile) PsiFileFactory.getInstance(project).createFileFromText(fileName, JetFileType.INSTANCE, text,
159 LocalTimeCounter.currentTime(), false);
160 }
161
162 @NotNull
163 public static JetFile createPhysicalFile(Project project, String fileName, String text) {
164 return (JetFile) PsiFileFactory.getInstance(project).createFileFromText(fileName, JetFileType.INSTANCE, text,
165 LocalTimeCounter.currentTime(), true);
166 }
167
168 @NotNull
169 public static JetProperty createProperty(Project project, String name, String type, boolean isVar, @Nullable String initializer) {
170 String text = (isVar ? "var " : "val ") + name + (type != null ? ":" + type : "") + (initializer == null ? "" : " = " + initializer);
171 return createProperty(project, text);
172 }
173
174 @NotNull
175 public static JetProperty createProperty(Project project, String name, String type, boolean isVar) {
176 return createProperty(project, name, type, isVar, null);
177 }
178
179 @NotNull
180 public static JetProperty createProperty(Project project, String text) {
181 return createDeclaration(project, text, JetProperty.class);
182 }
183
184 @NotNull
185 public static <T> T createDeclaration(Project project, String text, Class<T> clazz) {
186 JetFile file = createFile(project, text);
187 List<JetDeclaration> dcls = file.getDeclarations();
188 assert dcls.size() == 1 : dcls.size() + " declarations in " + text;
189 @SuppressWarnings("unchecked")
190 T result = (T) dcls.get(0);
191 return result;
192 }
193
194 @NotNull
195 public static PsiElement createNameIdentifier(Project project, String name) {
196 return createProperty(project, name, null, false).getNameIdentifier();
197 }
198
199 @NotNull
200 public static JetSimpleNameExpression createSimpleName(Project project, String name) {
201 return (JetSimpleNameExpression) createProperty(project, name, null, false, name).getInitializer();
202 }
203
204 @NotNull
205 public static PsiElement createIdentifier(Project project, String name) {
206 return createSimpleName(project, name).getIdentifier();
207 }
208
209 @NotNull
210 public static JetNamedFunction createFunction(Project project, String funDecl) {
211 return createDeclaration(project, funDecl, JetNamedFunction.class);
212 }
213
214 @NotNull
215 public static JetModifierList createModifierList(Project project, JetKeywordToken modifier) {
216 return createModifierList(project, modifier.getValue());
217 }
218
219 @NotNull
220 public static JetModifierList createModifierList(Project project, String text) {
221 JetProperty property = createProperty(project, text + " val x");
222 return property.getModifierList();
223 }
224
225 @NotNull
226 public static JetAnnotation createAnnotation(Project project, String text) {
227 JetProperty property = createProperty(project, text + " val x");
228 JetModifierList modifierList = property.getModifierList();
229 assert modifierList != null;
230 return modifierList.getAnnotations().get(0);
231 }
232
233 @NotNull
234 public static JetModifierList createConstructorModifierList(Project project, JetKeywordToken modifier) {
235 JetClass aClass = createClass(project, "class C " + modifier.getValue() + " (){}");
236 return aClass.getPrimaryConstructorModifierList();
237 }
238
239 @NotNull
240 public static JetExpression createEmptyBody(Project project) {
241 JetNamedFunction function = createFunction(project, "fun foo() {}");
242 return function.getBodyExpression();
243 }
244
245 @NotNull
246 public static JetClassBody createEmptyClassBody(Project project) {
247 JetClass aClass = createClass(project, "class A(){}");
248 return aClass.getBody();
249 }
250
251 @NotNull
252 public static JetParameter createParameter(Project project, String name, String type) {
253 JetNamedFunction function = createFunction(project, "fun foo(" + name + " : " + type + ") {}");
254 return function.getValueParameters().get(0);
255 }
256
257 @NotNull
258 public static JetParameterList createParameterList(Project project, String text) {
259 JetNamedFunction function = createFunction(project, "fun foo" + text + "{}");
260 return function.getValueParameterList();
261 }
262
263 @NotNull
264 public static JetWhenEntry createWhenEntry(@NotNull Project project, @NotNull String entryText) {
265 JetNamedFunction function = createFunction(project, "fun foo() { when(12) { " + entryText + " } }");
266 JetWhenEntry whenEntry = PsiTreeUtil.findChildOfType(function, JetWhenEntry.class);
267
268 assert whenEntry != null : "Couldn't generate when entry";
269 assert entryText.equals(whenEntry.getText()) : "Generate when entry text differs from the given text";
270
271 return whenEntry;
272 }
273
274 @NotNull
275 public static JetStringTemplateEntryWithExpression createBlockStringTemplateEntry(@NotNull Project project, @NotNull JetExpression expression) {
276 JetStringTemplateExpression stringTemplateExpression = (JetStringTemplateExpression) createExpression(project,
277 "\"${" + expression.getText() + "}\"");
278 return (JetStringTemplateEntryWithExpression) stringTemplateExpression.getEntries()[0];
279 }
280
281 @NotNull
282 public static JetImportDirective createImportDirective(Project project, @NotNull String path) {
283 return createImportDirective(project, new ImportPath(path));
284 }
285
286 @NotNull
287 public static JetImportDirective createImportDirective(Project project, @NotNull ImportPath importPath) {
288 if (importPath.fqnPart().isRoot()) {
289 throw new IllegalArgumentException("import path must not be empty");
290 }
291
292 StringBuilder importDirectiveBuilder = new StringBuilder("import ");
293 importDirectiveBuilder.append(importPath.getPathStr());
294
295 Name alias = importPath.getAlias();
296 if (alias != null) {
297 importDirectiveBuilder.append(" as ").append(alias.asString());
298 }
299
300 JetFile file = createFile(project, importDirectiveBuilder.toString());
301 return file.getImportDirectives().iterator().next();
302 }
303
304 @NotNull
305 public static JetImportList createImportDirectiveWithImportList(Project project, @NotNull ImportPath importPath) {
306 JetImportDirective importDirective = createImportDirective(project, importPath);
307 return (JetImportList) importDirective.getParent();
308 }
309
310 @NotNull
311 public static PsiElement createPrimaryConstructor(Project project) {
312 JetClass aClass = createClass(project, "class A()");
313 return aClass.findElementAt(7).getParent();
314 }
315
316 @NotNull
317 public static JetSimpleNameExpression createClassLabel(Project project, @NotNull String labelName) {
318 JetThisExpression expression = (JetThisExpression) createExpression(project, "this@" + labelName);
319 return expression.getTargetLabel();
320 }
321
322 @NotNull
323 public static JetExpression createFieldIdentifier(Project project, @NotNull String fieldName) {
324 return createExpression(project, "$" + fieldName);
325 }
326
327 @NotNull
328 public static JetBinaryExpression createBinaryExpression(Project project, @NotNull String lhs, @NotNull String op, @NotNull String rhs) {
329 return (JetBinaryExpression) createExpression(project, lhs + " " + op + " " + rhs);
330 }
331
332 @NotNull
333 public static JetBinaryExpression createBinaryExpression(Project project, @Nullable JetExpression lhs, @NotNull String op, @Nullable JetExpression rhs) {
334 return createBinaryExpression(project, JetPsiUtil.getText(lhs), op, JetPsiUtil.getText(rhs));
335 }
336
337 @NotNull
338 public static JetTypeCodeFragment createTypeCodeFragment(Project project, String text, PsiElement context) {
339 return new JetTypeCodeFragment(project, "fragment.kt", text, context);
340 }
341
342 @NotNull
343 public static JetExpressionCodeFragment createExpressionCodeFragment(Project project, String text, PsiElement context) {
344 return new JetExpressionCodeFragment(project, "fragment.kt", text, context);
345 }
346
347 @NotNull
348 public static JetBlockCodeFragment createBlockCodeFragment(Project project, String text, PsiElement context) {
349 return new JetBlockCodeFragment(project, "fragment.kt", text, context);
350 }
351
352 @NotNull
353 public static JetReturnExpression createReturn(Project project, @NotNull String text) {
354 return (JetReturnExpression) createExpression(project, "return " + text);
355 }
356
357 @NotNull
358 public static JetReturnExpression createReturn(Project project, @Nullable JetExpression expression) {
359 return createReturn(project, JetPsiUtil.getText(expression));
360 }
361
362 @NotNull
363 public static JetIfExpression createIf(Project project,
364 @Nullable JetExpression condition, @Nullable JetExpression thenExpr, @Nullable JetExpression elseExpr) {
365 return (JetIfExpression) createExpression(project, JetPsiUnparsingUtils.toIf(condition, thenExpr, elseExpr));
366 }
367
368 @NotNull
369 public static JetValueArgument createArgumentWithName(
370 @NotNull Project project,
371 @NotNull String name,
372 @NotNull JetExpression argumentExpression
373 ) {
374 return createCallArguments(project, "(" + name + " = " + argumentExpression.getText() + ")").getArguments().get(0);
375 }
376
377 public static class IfChainBuilder {
378 private final StringBuilder sb = new StringBuilder();
379 private boolean first = true;
380 private boolean frozen = false;
381
382 public IfChainBuilder() {
383 }
384
385 @NotNull
386 public IfChainBuilder ifBranch(@NotNull String conditionText, @NotNull String expressionText) {
387 if (first) {
388 first = false;
389 } else {
390 sb.append("else ");
391 }
392
393 sb.append("if (").append(conditionText).append(") ").append(expressionText).append("\n");
394 return this;
395 }
396
397 @NotNull
398 public IfChainBuilder ifBranch(@NotNull JetExpression condition, @NotNull JetExpression expression) {
399 return ifBranch(condition.getText(), expression.getText());
400 }
401
402 @NotNull
403 public IfChainBuilder elseBranch(@NotNull String expressionText) {
404 sb.append("else ").append(expressionText);
405 return this;
406 }
407
408 @NotNull
409 public IfChainBuilder elseBranch(@Nullable JetExpression expression) {
410 return elseBranch(JetPsiUtil.getText(expression));
411 }
412
413 @NotNull
414 public JetIfExpression toExpression(Project project) {
415 if (!frozen) {
416 frozen = true;
417 }
418 return (JetIfExpression) createExpression(project, sb.toString());
419 }
420 }
421
422 public static class WhenBuilder {
423 private final StringBuilder sb = new StringBuilder("when ");
424 private boolean frozen = false;
425 private boolean inCondition = false;
426
427 public WhenBuilder() {
428 this((String)null);
429 }
430
431 public WhenBuilder(@Nullable String subjectText) {
432 if (subjectText != null) {
433 sb.append("(").append(subjectText).append(") ");
434 }
435 sb.append("{\n");
436 }
437
438 public WhenBuilder(@Nullable JetExpression subject) {
439 this(subject != null ? subject.getText() : null);
440 }
441
442 @NotNull
443 public WhenBuilder condition(@NotNull String text) {
444 assert !frozen;
445
446 if (!inCondition) {
447 inCondition = true;
448 } else {
449 sb.append(", ");
450 }
451 sb.append(text);
452
453 return this;
454 }
455
456 @NotNull
457 public WhenBuilder condition(@Nullable JetExpression expression) {
458 return condition(JetPsiUtil.getText(expression));
459 }
460
461 @NotNull
462 public WhenBuilder pattern(@NotNull String typeReferenceText, boolean negated) {
463 return condition((negated ? "!is" : "is") + " " + typeReferenceText);
464 }
465
466 @NotNull
467 public WhenBuilder pattern(@Nullable JetTypeReference typeReference, boolean negated) {
468 return pattern(JetPsiUtil.getText(typeReference), negated);
469 }
470
471 @NotNull
472 public WhenBuilder range(@NotNull String argumentText, boolean negated) {
473 return condition((negated ? "!in" : "in") + " " + argumentText);
474 }
475
476 @NotNull
477 public WhenBuilder range(@Nullable JetExpression argument, boolean negated) {
478 return range(JetPsiUtil.getText(argument), negated);
479 }
480
481 @NotNull
482 public WhenBuilder branchExpression(@NotNull String expressionText) {
483 assert !frozen;
484 assert inCondition;
485
486 inCondition = false;
487 sb.append(" -> ").append(expressionText).append("\n");
488
489 return this;
490 }
491
492 @NotNull
493 public WhenBuilder branchExpression(@Nullable JetExpression expression) {
494 return branchExpression(JetPsiUtil.getText(expression));
495 }
496
497 @NotNull
498 public WhenBuilder entry(@NotNull String entryText) {
499 assert !frozen;
500 assert !inCondition;
501
502 sb.append(entryText).append("\n");
503
504 return this;
505 }
506
507 @NotNull
508 public WhenBuilder entry(@Nullable JetWhenEntry whenEntry) {
509 return entry(JetPsiUtil.getText(whenEntry));
510 }
511
512 @NotNull
513 public WhenBuilder elseEntry(@NotNull String text) {
514 return entry("else -> " + text);
515 }
516
517 @NotNull
518 public WhenBuilder elseEntry(@Nullable JetExpression expression) {
519 return elseEntry(JetPsiUtil.getText(expression));
520 }
521
522 @NotNull
523 public JetWhenExpression toExpression(Project project) {
524 if (!frozen) {
525 sb.append("}");
526 frozen = true;
527 }
528 return (JetWhenExpression) createExpression(project, sb.toString());
529 }
530 }
531
532 public static class FunctionBuilder {
533 static enum State {
534 MODIFIERS,
535 NAME,
536 RECEIVER,
537 FIRST_PARAM,
538 REST_PARAMS,
539 TYPE_CONSTRAINTS,
540 BODY,
541 DONE
542 }
543
544 private final StringBuilder sb = new StringBuilder();
545 private State state = State.MODIFIERS;
546
547 public FunctionBuilder() {
548 }
549
550 private void closeParams() {
551 assert state == State.FIRST_PARAM || state == State.REST_PARAMS;
552
553 sb.append(")");
554
555 state = State.TYPE_CONSTRAINTS;
556 }
557
558 private void placeFun() {
559 assert state == State.MODIFIERS;
560
561 if (sb.length() != 0) {
562 sb.append(" ");
563 }
564 sb.append("fun ");
565
566 state = State.RECEIVER;
567 }
568
569 @NotNull
570 public FunctionBuilder modifier(@NotNull String modifier) {
571 assert state == State.MODIFIERS;
572
573 sb.append(modifier);
574
575 return this;
576 }
577
578 @NotNull
579 public FunctionBuilder typeParams(@NotNull Collection<String> values) {
580 placeFun();
581 if (!values.isEmpty()) {
582 sb.append(KotlinPackage.makeString(values, ", ", "<", "> ", -1, ""));
583 }
584
585 return this;
586 }
587
588 @NotNull
589 public FunctionBuilder receiver(@NotNull String receiverType) {
590 assert state == State.RECEIVER;
591
592 sb.append(receiverType).append(".");
593 state = State.NAME;
594
595 return this;
596 }
597
598 @NotNull
599 public FunctionBuilder name(@NotNull String name) {
600 assert state == State.NAME || state == State.RECEIVER;
601
602 sb.append(name).append("(");
603 state = State.FIRST_PARAM;
604
605 return this;
606 }
607
608 @NotNull
609 public FunctionBuilder param(@NotNull String name, @NotNull String type) {
610 assert state == State.FIRST_PARAM || state == State.REST_PARAMS;
611
612 if (state == State.REST_PARAMS) {
613 sb.append(", ");
614 }
615 sb.append(name).append(": ").append(type);
616 if (state == State.FIRST_PARAM) {
617 state = State.REST_PARAMS;
618 }
619
620 return this;
621 }
622
623 @NotNull
624 public FunctionBuilder returnType(@NotNull String type) {
625 closeParams();
626 sb.append(": ").append(type);
627
628 return this;
629 }
630
631 @NotNull
632 public FunctionBuilder noReturnType() {
633 closeParams();
634
635 return this;
636 }
637
638 @NotNull
639 public FunctionBuilder typeConstraints(@NotNull Collection<String> values) {
640 assert state == State.TYPE_CONSTRAINTS;
641
642 if (!values.isEmpty()) {
643 sb.append(KotlinPackage.makeString(values, ", ", " where ", "", -1, ""));
644 }
645 state = State.BODY;
646
647 return this;
648 }
649
650 @NotNull
651 public FunctionBuilder simpleBody(@NotNull String body) {
652 assert state == State.BODY || state == State.TYPE_CONSTRAINTS;
653
654 sb.append(" = ").append(body);
655 state = State.DONE;
656
657 return this;
658 }
659
660 @NotNull
661 public FunctionBuilder blockBody(@NotNull String body) {
662 assert state == State.BODY || state == State.TYPE_CONSTRAINTS;
663
664 sb.append(" {\n").append(body).append("\n}");
665 state = State.DONE;
666
667 return this;
668 }
669
670 @NotNull
671 public String toFunctionText() {
672 if (state != State.DONE) {
673 state = State.DONE;
674 }
675
676 return sb.toString();
677 }
678 }
679
680 @NotNull
681 public static JetExpression createFunctionBody(Project project, @NotNull String bodyText) {
682 JetFunction func = createFunction(project, "fun foo() {\n" + bodyText + "\n}");
683 return func.getBodyExpression();
684 }
685
686 @NotNull
687 public static JetClassObject createEmptyClassObject(Project project) {
688 JetClass klass = createClass(project, "class foo { class object { } }");
689 return klass.getClassObject();
690 }
691
692 @NotNull
693 public static JetBlockExpression wrapInABlock(@NotNull JetExpression expression) {
694 if (expression instanceof JetBlockExpression) {
695 return (JetBlockExpression) expression;
696 }
697 return BlockWrapper.create(expression);
698 }
699
700 private static class BlockWrapper extends JetBlockExpression implements JetPsiUtil.JetExpressionWrapper {
701 private final JetExpression expression;
702
703 public static BlockWrapper create(@NotNull JetExpression expressionToWrap) {
704 JetNamedFunction function = createFunction(expressionToWrap.getProject(), "fun f() { " + expressionToWrap.getText() + "}");
705 JetBlockExpression block = (JetBlockExpression) function.getBodyExpression();
706 assert block != null;
707 return new BlockWrapper(block, expressionToWrap);
708 }
709
710 private BlockWrapper(@NotNull JetBlockExpression fakeBlockExpression, @NotNull JetExpression expressionToWrap) {
711 super(fakeBlockExpression.getNode());
712 this.expression = expressionToWrap;
713 }
714
715 @NotNull
716 @Override
717 public List<JetElement> getStatements() {
718 return Collections.<JetElement>singletonList(expression);
719 }
720
721 @Override
722 public JetExpression getBaseExpression() {
723 return expression;
724 }
725 }
726 }