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 017package org.jetbrains.jet.lang.psi; 018 019import com.intellij.lang.ASTNode; 020import com.intellij.openapi.project.Project; 021import com.intellij.openapi.util.Pair; 022import com.intellij.psi.PsiElement; 023import com.intellij.psi.PsiFileFactory; 024import com.intellij.psi.util.PsiTreeUtil; 025import com.intellij.util.LocalTimeCounter; 026import org.jetbrains.annotations.NotNull; 027import org.jetbrains.annotations.Nullable; 028import org.jetbrains.jet.lang.resolve.ImportPath; 029import org.jetbrains.jet.lang.resolve.name.Name; 030import org.jetbrains.jet.lexer.JetKeywordToken; 031import org.jetbrains.jet.lexer.JetTokens; 032import org.jetbrains.jet.plugin.JetFileType; 033 034import java.util.List; 035 036public 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}