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 }