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 }