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