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.google.common.base.Function; 020import com.google.common.base.Predicate; 021import com.google.common.collect.Lists; 022import com.intellij.lang.ASTNode; 023import com.intellij.psi.PsiComment; 024import com.intellij.psi.PsiElement; 025import com.intellij.psi.PsiFile; 026import com.intellij.psi.PsiWhiteSpace; 027import com.intellij.psi.impl.CheckUtil; 028import com.intellij.psi.impl.source.codeStyle.CodeEditUtil; 029import com.intellij.psi.tree.IElementType; 030import com.intellij.psi.util.PsiTreeUtil; 031import com.intellij.util.codeInsight.CommentUtilCore; 032import com.intellij.util.containers.ContainerUtil; 033import org.jetbrains.annotations.NotNull; 034import org.jetbrains.annotations.Nullable; 035import org.jetbrains.jet.JetNodeTypes; 036import org.jetbrains.jet.kdoc.psi.api.KDocElement; 037import org.jetbrains.jet.lang.parsing.JetExpressionParsing; 038import org.jetbrains.jet.lang.resolve.ImportPath; 039import org.jetbrains.jet.lang.resolve.name.FqName; 040import org.jetbrains.jet.lang.resolve.name.Name; 041import org.jetbrains.jet.lang.types.expressions.OperatorConventions; 042import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns; 043import org.jetbrains.jet.lexer.JetToken; 044import org.jetbrains.jet.lexer.JetTokens; 045 046import java.util.Collection; 047import java.util.HashSet; 048import java.util.List; 049import java.util.Set; 050 051public class JetPsiUtil { 052 053 public static final Name NO_NAME_PROVIDED = Name.special("<no name provided>"); 054 public static final Name ROOT_NAMESPACE_NAME = Name.special("<root namespace>"); 055 056 private JetPsiUtil() { 057 } 058 059 public static <D> void visitChildren(@NotNull JetElement element, @NotNull JetTreeVisitor<D> visitor, D data) { 060 PsiElement child = element.getFirstChild(); 061 while (child != null) { 062 if (child instanceof JetElement) { 063 ((JetElement) child).accept(visitor, data); 064 } 065 child = child.getNextSibling(); 066 } 067 } 068 069 @Nullable 070 public static JetExpression deparenthesizeWithNoTypeResolution(@NotNull JetExpression expression) { 071 return deparenthesizeWithResolutionStrategy(expression, null); 072 } 073 074 @Nullable 075 @Deprecated //Use JetPsiUtil.deparenthesizeWithNoTypeResolution() or ExpressionTypingServices.deparenthesize() 076 public static JetExpression deparenthesizeWithResolutionStrategy( 077 @NotNull JetExpression expression, 078 @Nullable Function<JetTypeReference, Void> typeResolutionStrategy 079 ) { 080 if (expression instanceof JetBinaryExpressionWithTypeRHS) { 081 JetBinaryExpressionWithTypeRHS binaryExpression = (JetBinaryExpressionWithTypeRHS) expression; 082 JetSimpleNameExpression operationSign = binaryExpression.getOperationReference(); 083 if (JetTokens.COLON.equals(operationSign.getReferencedNameElementType())) { 084 expression = binaryExpression.getLeft(); 085 JetTypeReference typeReference = binaryExpression.getRight(); 086 if (typeResolutionStrategy != null && typeReference != null) { 087 typeResolutionStrategy.apply(typeReference); 088 } 089 } 090 } 091 else if (expression instanceof JetPrefixExpression) { 092 if (JetTokens.LABELS.contains(((JetPrefixExpression) expression).getOperationReference().getReferencedNameElementType())) { 093 JetExpression baseExpression = ((JetPrefixExpression) expression).getBaseExpression(); 094 if (baseExpression != null) { 095 expression = baseExpression; 096 } 097 } 098 } 099 if (expression instanceof JetParenthesizedExpression) { 100 JetExpression innerExpression = ((JetParenthesizedExpression) expression).getExpression(); 101 return innerExpression != null ? deparenthesizeWithResolutionStrategy(innerExpression, typeResolutionStrategy) : null; 102 } 103 return expression; 104 } 105 106 @NotNull 107 public static Name safeName(@Nullable String name) { 108 return name == null ? NO_NAME_PROVIDED : Name.identifier(name); 109 } 110 111 @NotNull 112 public static Set<JetElement> findRootExpressions(@NotNull Collection<JetElement> unreachableElements) { 113 Set<JetElement> rootElements = new HashSet<JetElement>(); 114 final Set<JetElement> shadowedElements = new HashSet<JetElement>(); 115 JetVisitorVoid shadowAllChildren = new JetVisitorVoid() { 116 @Override 117 public void visitJetElement(JetElement element) { 118 if (shadowedElements.add(element)) { 119 element.acceptChildren(this); 120 } 121 } 122 }; 123 124 for (JetElement element : unreachableElements) { 125 if (shadowedElements.contains(element)) continue; 126 element.acceptChildren(shadowAllChildren); 127 128 rootElements.removeAll(shadowedElements); 129 rootElements.add(element); 130 } 131 return rootElements; 132 } 133 134 @NotNull 135 public static String unquoteIdentifier(@NotNull String quoted) { 136 if (quoted.indexOf('`') < 0) { 137 return quoted; 138 } 139 140 if (quoted.startsWith("`") && quoted.endsWith("`") && quoted.length() >= 2) { 141 return quoted.substring(1, quoted.length() - 1); 142 } 143 else { 144 return quoted; 145 } 146 } 147 148 @NotNull 149 public static String unquoteIdentifierOrFieldReference(@NotNull String quoted) { 150 if (quoted.indexOf('`') < 0) { 151 return quoted; 152 } 153 154 if (quoted.startsWith("$")) { 155 return "$" + unquoteIdentifier(quoted.substring(1)); 156 } 157 else { 158 return unquoteIdentifier(quoted); 159 } 160 } 161 162 @NotNull 163 public static FqName getFQName(@NotNull JetFile file) { 164 JetNamespaceHeader header = file.getNamespaceHeader(); 165 return header != null ? header.getFqName() : FqName.ROOT; 166 } 167 168 @Nullable 169 public static FqName getFQName(@NotNull JetNamedDeclaration namedDeclaration) { 170 if (namedDeclaration instanceof JetObjectDeclarationName) { 171 JetNamedDeclaration objectDeclaration = PsiTreeUtil.getParentOfType(namedDeclaration, JetObjectDeclaration.class); 172 if (objectDeclaration == null) { 173 objectDeclaration = PsiTreeUtil.getParentOfType(namedDeclaration, JetEnumEntry.class); 174 } 175 176 if (objectDeclaration == null) { 177 return null; 178 } 179 180 return getFQName(objectDeclaration); 181 } 182 183 Name name = namedDeclaration.getNameAsName(); 184 if (name == null) { 185 return null; 186 } 187 188 PsiElement parent = namedDeclaration.getParent(); 189 if (parent instanceof JetClassBody) { 190 // One nesting to JetClassBody doesn't affect to qualified name 191 parent = parent.getParent(); 192 } 193 194 FqName firstPart = null; 195 if (parent instanceof JetFile) { 196 firstPart = getFQName((JetFile) parent); 197 } 198 else if (parent instanceof JetNamedFunction || parent instanceof JetClass) { 199 firstPart = getFQName((JetNamedDeclaration) parent); 200 } 201 else if (namedDeclaration instanceof JetParameter) { 202 JetClass constructorClass = getClassIfParameterIsProperty((JetParameter) namedDeclaration); 203 if (constructorClass != null) { 204 firstPart = getFQName(constructorClass); 205 } 206 } 207 else if (parent instanceof JetObjectDeclaration) { 208 if (parent.getParent() instanceof JetClassObject) { 209 JetClassOrObject classOrObject = PsiTreeUtil.getParentOfType(parent, JetClassOrObject.class); 210 if (classOrObject != null) { 211 firstPart = getFQName(classOrObject); 212 } 213 } 214 else { 215 firstPart = getFQName((JetNamedDeclaration) parent); 216 } 217 } 218 219 if (firstPart == null) { 220 return null; 221 } 222 223 return firstPart.child(name); 224 } 225 226 /** @return <code>null</code> iff the tye has syntactic errors */ 227 @Nullable 228 public static FqName toQualifiedName(@NotNull JetUserType userType) { 229 List<String> reversedNames = Lists.newArrayList(); 230 231 JetUserType current = userType; 232 while (current != null) { 233 String name = current.getReferencedName(); 234 if (name == null) return null; 235 236 reversedNames.add(name); 237 current = current.getQualifier(); 238 } 239 240 return FqName.fromSegments(ContainerUtil.reverse(reversedNames)); 241 } 242 243 @Nullable 244 public static Name getShortName(@NotNull JetAnnotationEntry annotation) { 245 JetTypeReference typeReference = annotation.getTypeReference(); 246 assert typeReference != null : "Annotation entry hasn't typeReference " + annotation.getText(); 247 JetTypeElement typeElement = typeReference.getTypeElement(); 248 if (typeElement instanceof JetUserType) { 249 JetUserType userType = (JetUserType) typeElement; 250 String shortName = userType.getReferencedName(); 251 if (shortName != null) { 252 return Name.identifier(shortName); 253 } 254 } 255 return null; 256 } 257 258 public static boolean isDeprecated(@NotNull JetModifierListOwner owner) { 259 JetModifierList modifierList = owner.getModifierList(); 260 if (modifierList != null) { 261 List<JetAnnotationEntry> annotationEntries = modifierList.getAnnotationEntries(); 262 for (JetAnnotationEntry annotation : annotationEntries) { 263 Name shortName = getShortName(annotation); 264 if (KotlinBuiltIns.getInstance().getDeprecatedAnnotation().getName().equals(shortName)) { 265 return true; 266 } 267 } 268 } 269 return false; 270 } 271 272 @Nullable 273 @IfNotParsed 274 public static ImportPath getImportPath(@NotNull JetImportDirective importDirective) { 275 if (PsiTreeUtil.hasErrorElements(importDirective)) { 276 return null; 277 } 278 279 FqName importFqn = getFQName(importDirective.getImportedReference()); 280 if (importFqn == null) { 281 return null; 282 } 283 284 Name alias = null; 285 String aliasName = importDirective.getAliasName(); 286 if (aliasName != null) { 287 alias = Name.identifier(aliasName); 288 } 289 290 return new ImportPath(importFqn, importDirective.isAllUnder(), alias); 291 } 292 293 @Nullable 294 public static <T extends PsiElement> T getDirectParentOfTypeForBlock(@NotNull JetBlockExpression block, @NotNull Class<T> aClass) { 295 T parent = PsiTreeUtil.getParentOfType(block, aClass); 296 if (parent instanceof JetIfExpression) { 297 JetIfExpression ifExpression = (JetIfExpression) parent; 298 if (ifExpression.getElse() == block || ifExpression.getThen() == block) { 299 return parent; 300 } 301 } 302 if (parent instanceof JetWhenExpression) { 303 JetWhenExpression whenExpression = (JetWhenExpression) parent; 304 for (JetWhenEntry whenEntry : whenExpression.getEntries()) { 305 if (whenEntry.getExpression() == block) { 306 return parent; 307 } 308 } 309 } 310 if (parent instanceof JetFunctionLiteral) { 311 JetFunctionLiteral functionLiteral = (JetFunctionLiteral) parent; 312 if (functionLiteral.getBodyExpression() == block) { 313 return parent; 314 } 315 } 316 if (parent instanceof JetTryExpression) { 317 JetTryExpression tryExpression = (JetTryExpression) parent; 318 if (tryExpression.getTryBlock() == block) { 319 return parent; 320 } 321 for (JetCatchClause clause : tryExpression.getCatchClauses()) { 322 if (clause.getCatchBody() == block) { 323 return parent; 324 } 325 } 326 } 327 return null; 328 } 329 330 public static boolean isImplicitlyUsed(@NotNull JetElement element) { 331 PsiElement parent = element.getParent(); 332 if (!(parent instanceof JetBlockExpression)) return true; 333 JetBlockExpression block = (JetBlockExpression) parent; 334 List<JetElement> statements = block.getStatements(); 335 if (statements.get(statements.size() - 1) == element) { 336 JetExpression expression = getDirectParentOfTypeForBlock(block, JetIfExpression.class); 337 if (expression == null) { 338 expression = getDirectParentOfTypeForBlock(block, JetWhenExpression.class); 339 } 340 if (expression == null) { 341 expression = getDirectParentOfTypeForBlock(block, JetFunctionLiteral.class); 342 } 343 if (expression == null) { 344 expression = getDirectParentOfTypeForBlock(block, JetTryExpression.class); 345 } 346 if (expression != null) { 347 return isImplicitlyUsed(expression); 348 } 349 } 350 return false; 351 } 352 353 public static void deleteClass(@NotNull JetClassOrObject clazz) { 354 CheckUtil.checkWritable(clazz); 355 JetFile file = (JetFile) clazz.getContainingFile(); 356 List<JetDeclaration> declarations = file.getDeclarations(); 357 if (declarations.size() == 1) { 358 file.delete(); 359 } 360 else { 361 PsiElement parent = clazz.getParent(); 362 CodeEditUtil.removeChild(parent.getNode(), clazz.getNode()); 363 } 364 } 365 366 @Nullable 367 public static Name getAliasName(@NotNull JetImportDirective importDirective) { 368 String aliasName = importDirective.getAliasName(); 369 JetExpression importedReference = importDirective.getImportedReference(); 370 if (importedReference == null) { 371 return null; 372 } 373 JetSimpleNameExpression referenceExpression = getLastReference(importedReference); 374 if (aliasName == null) { 375 aliasName = referenceExpression != null ? referenceExpression.getReferencedName() : null; 376 } 377 378 return aliasName != null && !aliasName.isEmpty() ? Name.identifier(aliasName) : null; 379 } 380 381 @Nullable 382 public static JetSimpleNameExpression getLastReference(@NotNull JetExpression importedReference) { 383 if (importedReference instanceof JetDotQualifiedExpression) { 384 JetExpression selectorExpression = ((JetDotQualifiedExpression) importedReference).getSelectorExpression(); 385 return (selectorExpression instanceof JetSimpleNameExpression) ? (JetSimpleNameExpression) selectorExpression : null; 386 } 387 if (importedReference instanceof JetSimpleNameExpression) { 388 return (JetSimpleNameExpression) importedReference; 389 } 390 return null; 391 } 392 393 public static boolean isFirstPartInQualified(@NotNull JetSimpleNameExpression nameExpression) { 394 @SuppressWarnings("unchecked") JetUserType userType = PsiTreeUtil.getParentOfType(nameExpression, JetUserType.class, true, 395 JetDeclaration.class); 396 if (userType != null) { 397 return PsiTreeUtil.isAncestor(userType.getFirstChild(), nameExpression, false); 398 } 399 400 @SuppressWarnings("unchecked") JetQualifiedExpression qualifiedExpression = PsiTreeUtil.getParentOfType(nameExpression, JetQualifiedExpression.class, true, JetDeclaration.class); 401 if (qualifiedExpression != null) { 402 return PsiTreeUtil.isAncestor(qualifiedExpression.getFirstChild(), nameExpression, false); 403 } 404 405 return true; 406 } 407 408 public static boolean isVoidType(@Nullable JetTypeReference typeReference) { 409 if (typeReference == null) { 410 return false; 411 } 412 413 return KotlinBuiltIns.getInstance().getUnit().getName().asString().equals(typeReference.getText()); 414 } 415 416 public static boolean isSafeCall(@NotNull Call call) { 417 ASTNode callOperationNode = call.getCallOperationNode(); 418 return callOperationNode != null && callOperationNode.getElementType() == JetTokens.SAFE_ACCESS; 419 } 420 421 public static boolean isFunctionLiteralWithoutDeclaredParameterTypes(@Nullable JetExpression expression) { 422 if (!(expression instanceof JetFunctionLiteralExpression)) return false; 423 JetFunctionLiteralExpression functionLiteral = (JetFunctionLiteralExpression) expression; 424 for (JetParameter parameter : functionLiteral.getValueParameters()) { 425 if (parameter.getTypeReference() != null) { 426 return false; 427 } 428 } 429 return true; 430 } 431 432 public static boolean isScriptDeclaration(@NotNull JetDeclaration namedDeclaration) { 433 return getScript(namedDeclaration) != null; 434 } 435 436 @Nullable 437 public static JetScript getScript(@NotNull JetDeclaration namedDeclaration) { 438 PsiElement parent = namedDeclaration.getParent(); 439 if (parent != null && parent.getParent() instanceof JetScript) { 440 return (JetScript) parent.getParent(); 441 } 442 else { 443 return null; 444 } 445 } 446 447 public static boolean isVariableNotParameterDeclaration(@NotNull JetDeclaration declaration) { 448 if (!(declaration instanceof JetVariableDeclaration)) return false; 449 if (declaration instanceof JetProperty) return true; 450 assert declaration instanceof JetMultiDeclarationEntry; 451 JetMultiDeclarationEntry multiDeclarationEntry = (JetMultiDeclarationEntry) declaration; 452 return !(multiDeclarationEntry.getParent().getParent() instanceof JetForExpression); 453 } 454 455 @Nullable 456 public static Name getConventionName(@NotNull JetSimpleNameExpression simpleNameExpression) { 457 if (simpleNameExpression.getIdentifier() != null) { 458 return simpleNameExpression.getReferencedNameAsName(); 459 } 460 461 PsiElement firstChild = simpleNameExpression.getFirstChild(); 462 if (firstChild != null) { 463 IElementType elementType = firstChild.getNode().getElementType(); 464 if (elementType instanceof JetToken) { 465 JetToken jetToken = (JetToken) elementType; 466 return OperatorConventions.getNameForOperationSymbol(jetToken); 467 } 468 } 469 470 return null; 471 } 472 473 @Nullable 474 public static PsiElement getTopmostParentOfTypes(@Nullable PsiElement element, @NotNull Class<? extends PsiElement>... parentTypes) { 475 if (element == null) { 476 return null; 477 } 478 479 PsiElement result = null; 480 PsiElement parent = element.getParent(); 481 while (parent != null) { 482 if (PsiTreeUtil.instanceOf(parent, parentTypes)) { 483 result = parent; 484 } 485 486 parent = parent.getParent(); 487 } 488 489 return result; 490 } 491 492 public static boolean isNullConstant(@NotNull JetExpression expression) { 493 JetExpression deparenthesized = deparenthesizeWithNoTypeResolution(expression); 494 return deparenthesized instanceof JetConstantExpression && deparenthesized.getNode().getElementType() == JetNodeTypes.NULL; 495 } 496 497 public static boolean isAbstract(@NotNull JetDeclarationWithBody declaration) { 498 return declaration.getBodyExpression() == null; 499 } 500 501 public static boolean isBackingFieldReference(@NotNull JetSimpleNameExpression expression) { 502 return expression.getReferencedNameElementType() == JetTokens.FIELD_IDENTIFIER; 503 } 504 505 public static boolean isBackingFieldReference(@Nullable JetElement element) { 506 return element instanceof JetSimpleNameExpression && isBackingFieldReference((JetSimpleNameExpression)element); 507 } 508 509 @Nullable 510 private static FqName getFQName(@Nullable JetExpression expression) { 511 if (expression == null) { 512 return null; 513 } 514 515 if (expression instanceof JetDotQualifiedExpression) { 516 JetDotQualifiedExpression dotQualifiedExpression = (JetDotQualifiedExpression) expression; 517 FqName parentFqn = getFQName(dotQualifiedExpression.getReceiverExpression()); 518 Name child = getName(dotQualifiedExpression.getSelectorExpression()); 519 520 return parentFqn != null && child != null ? parentFqn.child(child) : null; 521 } 522 else if (expression instanceof JetSimpleNameExpression) { 523 JetSimpleNameExpression simpleNameExpression = (JetSimpleNameExpression) expression; 524 return FqName.topLevel(simpleNameExpression.getReferencedNameAsName()); 525 } 526 else { 527 throw new IllegalArgumentException("Can't construct fqn for: " + expression.getClass().toString()); 528 } 529 } 530 531 @Nullable 532 private static Name getName(@Nullable JetExpression expression) { 533 if (expression == null) { 534 return null; 535 } 536 537 if (expression instanceof JetSimpleNameExpression) { 538 return ((JetSimpleNameExpression) expression).getReferencedNameAsName(); 539 } 540 else { 541 throw new IllegalArgumentException("Can't construct name for: " + expression.getClass().toString()); 542 } 543 } 544 545 @Nullable 546 public static JetElement getLastStatementInABlock(@Nullable JetBlockExpression blockExpression) { 547 if (blockExpression == null) return null; 548 List<JetElement> statements = blockExpression.getStatements(); 549 return statements.isEmpty() ? null : statements.get(statements.size() - 1); 550 } 551 552 public static boolean isLocalClass(@NotNull JetClassOrObject classOrObject) { 553 return getOutermostClassOrObject(classOrObject) == null; 554 } 555 556 public static boolean isTrait(@NotNull JetClassOrObject classOrObject) { 557 return classOrObject instanceof JetClass && ((JetClass) classOrObject).isTrait(); 558 } 559 560 @Nullable 561 public static JetClassOrObject getOutermostClassOrObject(@NotNull JetClassOrObject classOrObject) { 562 JetClassOrObject current = classOrObject; 563 while (true) { 564 PsiElement parent = current.getParent(); 565 assert classOrObject.getParent() != null : "Class with no parent: " + classOrObject.getText(); 566 567 if (parent instanceof PsiFile) { 568 return current; 569 } 570 if (parent instanceof JetClassObject) { 571 // current class IS the class object declaration 572 parent = parent.getParent(); 573 assert parent instanceof JetClassBody : "Parent of class object is not a class body: " + parent; 574 } 575 if (!(parent instanceof JetClassBody)) { 576 // It is a local class, no legitimate outer 577 return null; 578 } 579 580 current = (JetClassOrObject) parent.getParent(); 581 } 582 } 583 584 @Nullable 585 public static JetClass getClassIfParameterIsProperty(@NotNull JetParameter jetParameter) { 586 if (jetParameter.getValOrVarNode() != null) { 587 PsiElement parent = jetParameter.getParent(); 588 if (parent instanceof JetParameterList && parent.getParent() instanceof JetClass) { 589 return (JetClass) parent.getParent(); 590 } 591 } 592 593 return null; 594 } 595 596 @Nullable 597 private static IElementType getOperation(@NotNull JetExpression expression) { 598 if (expression instanceof JetQualifiedExpression) { 599 return ((JetQualifiedExpression) expression).getOperationSign(); 600 } 601 else if (expression instanceof JetOperationExpression) { 602 return ((JetOperationExpression) expression).getOperationReference().getReferencedNameElementType(); 603 } 604 return null; 605 } 606 607 private static int getPrecedenceOfOperation(@NotNull JetExpression expression, @NotNull IElementType operation) { 608 if (expression instanceof JetPostfixExpression) return 0; 609 if (expression instanceof JetQualifiedExpression) return 0; 610 if (expression instanceof JetPrefixExpression) return 1; 611 612 for (JetExpressionParsing.Precedence precedence : JetExpressionParsing.Precedence.values()) { 613 if (precedence != JetExpressionParsing.Precedence.PREFIX && precedence != JetExpressionParsing.Precedence.POSTFIX && 614 precedence.getOperations().contains(operation)) { 615 return precedence.ordinal(); 616 } 617 } 618 throw new IllegalStateException("Unknown operation"); 619 } 620 621 public static boolean areParenthesesUseless(@NotNull JetParenthesizedExpression expression) { 622 JetExpression innerExpression = expression.getExpression(); 623 JetExpression parentExpression = PsiTreeUtil.getParentOfType(expression, JetExpression.class, true); 624 if (innerExpression == null || parentExpression == null) return true; 625 626 IElementType innerOperation = getOperation(innerExpression); 627 IElementType parentOperation = getOperation(parentExpression); 628 629 // 'return (@label{...})' case 630 if (parentExpression instanceof JetReturnExpression && innerOperation == JetTokens.LABEL_IDENTIFIER) { 631 return false; 632 } 633 634 // '(x: Int) < y' case 635 if (innerExpression instanceof JetBinaryExpressionWithTypeRHS && parentOperation == JetTokens.LT) { 636 return false; 637 } 638 639 // associative operations 640 if (innerOperation == parentOperation && (innerOperation == JetTokens.OROR || innerOperation == JetTokens.ANDAND)) { 641 return true; 642 } 643 644 if (innerOperation == null) return true; 645 if (parentExpression instanceof JetArrayAccessExpression) { 646 return ((JetArrayAccessExpression) parentExpression).getArrayExpression() != expression; 647 } 648 if (parentOperation == null) return true; 649 650 int innerPrecedence = getPrecedenceOfOperation(innerExpression, innerOperation); 651 int parentPrecedence = getPrecedenceOfOperation(parentExpression, parentOperation); 652 return innerPrecedence < parentPrecedence; 653 } 654 655 public static boolean isAssignment(@NotNull PsiElement element) { 656 return element instanceof JetBinaryExpression && 657 JetTokens.ALL_ASSIGNMENTS.contains(((JetBinaryExpression) element).getOperationToken()); 658 } 659 660 public static boolean isOrdinaryAssignment(@NotNull PsiElement element) { 661 return element instanceof JetBinaryExpression && 662 ((JetBinaryExpression) element).getOperationToken().equals(JetTokens.EQ); 663 } 664 665 @Nullable 666 public static JetElement getOutermostLastBlockElement(@Nullable JetElement element, @NotNull Predicate<JetElement> checkElement) { 667 if (element == null) return null; 668 669 if (!(element instanceof JetBlockExpression)) return checkElement.apply(element) ? element : null; 670 671 JetBlockExpression block = (JetBlockExpression)element; 672 int n = block.getStatements().size(); 673 674 if (n == 0) return null; 675 676 JetElement lastElement = block.getStatements().get(n - 1); 677 return checkElement.apply(lastElement) ? lastElement : null; 678 } 679 680 @Nullable 681 public static PsiElement getParentByTypeAndPredicate( 682 @Nullable PsiElement element, @NotNull Class<? extends PsiElement> aClass, @NotNull Predicate<PsiElement> predicate, boolean strict) { 683 if (element == null) return null; 684 if (strict) { 685 element = element.getParent(); 686 } 687 688 while (element != null) { 689 //noinspection unchecked 690 if (aClass.isInstance(element) && predicate.apply(element)) { 691 //noinspection unchecked 692 return element; 693 } 694 if (element instanceof PsiFile) return null; 695 element = element.getParent(); 696 } 697 698 return null; 699 } 700 701 public static boolean checkVariableDeclarationInBlock(@NotNull JetBlockExpression block, @NotNull String varName) { 702 for (JetElement element : block.getStatements()) { 703 if (element instanceof JetVariableDeclaration) { 704 if (((JetVariableDeclaration) element).getNameAsSafeName().asString().equals(varName)) { 705 return true; 706 } 707 } 708 } 709 710 return false; 711 } 712 713 public static boolean checkWhenExpressionHasSingleElse(@NotNull JetWhenExpression whenExpression) { 714 int elseCount = 0; 715 for (JetWhenEntry entry : whenExpression.getEntries()) { 716 if (entry.isElse()) { 717 elseCount++; 718 } 719 } 720 return (elseCount == 1); 721 } 722 723 @Nullable 724 public static PsiElement skipTrailingWhitespacesAndComments(@Nullable PsiElement element) { 725 return PsiTreeUtil.skipSiblingsForward(element, PsiWhiteSpace.class, PsiComment.class); 726 } 727 728 public static final Predicate<JetElement> ANY_JET_ELEMENT = new Predicate<JetElement>() { 729 @Override 730 public boolean apply(@Nullable JetElement input) { 731 return true; 732 } 733 }; 734 735 @NotNull 736 public static String getText(@Nullable PsiElement element) { 737 return element != null ? element.getText() : ""; 738 } 739 740 @Nullable 741 public static String getNullableText(@Nullable PsiElement element) { 742 return element != null ? element.getText() : null; 743 } 744 745 /** 746 * CommentUtilCore.isComment fails if element <strong>inside</strong> comment. 747 * 748 * Also, we can not add KDocTokens to COMMENTS TokenSet, because it is used in JetParserDefinition.getCommentTokens(), 749 * and therefor all COMMENTS tokens will be ignored by PsiBuilder. 750 * 751 * @param element 752 * @return 753 */ 754 public static boolean isInComment(PsiElement element) { 755 return CommentUtilCore.isComment(element) || element instanceof KDocElement; 756 } 757 758 @Nullable 759 public static PsiElement getOutermostParent(@NotNull PsiElement element, @NotNull PsiElement upperBound, boolean strict) { 760 PsiElement parent = strict ? element.getParent() : element; 761 while (parent != null && parent.getParent() != upperBound) { 762 parent = parent.getParent(); 763 } 764 765 return parent; 766 } 767 768 public static <T extends PsiElement> T getLastChildByType(@NotNull PsiElement root, @NotNull Class<? extends T>... elementTypes) { 769 PsiElement[] children = root.getChildren(); 770 771 for (int i = children.length - 1; i >= 0; i--) { 772 if (PsiTreeUtil.instanceOf(children[i], elementTypes)) { 773 //noinspection unchecked 774 return (T) children[i]; 775 } 776 } 777 778 return null; 779 } 780 781 @Nullable 782 public static <T extends JetElement> T getOutermostJetElement( 783 @Nullable PsiElement root, 784 boolean first, 785 @NotNull final Class<? extends T>... elementTypes 786 ) { 787 if (!(root instanceof JetElement)) return null; 788 789 final List<T> results = Lists.newArrayList(); 790 791 ((JetElement) root).accept( 792 new JetVisitorVoid() { 793 @Override 794 public void visitJetElement(JetElement element) { 795 if (PsiTreeUtil.instanceOf(element, elementTypes)) { 796 //noinspection unchecked 797 results.add((T) element); 798 } 799 else { 800 element.acceptChildren(this); 801 } 802 } 803 } 804 ); 805 806 if (results.isEmpty()) return null; 807 808 return first ? results.get(0) : results.get(results.size() - 1); 809 } 810 811 @Nullable 812 public static PsiElement findChildByType(@NotNull PsiElement element, @NotNull IElementType type) { 813 ASTNode node = element.getNode().findChildByType(type); 814 return node == null ? null : node.getPsi(); 815 } 816 817 @Nullable 818 public static JetExpression getCalleeExpressionIfAny(@NotNull JetExpression expression) { 819 if (expression instanceof JetCallElement) { 820 JetCallElement callExpression = (JetCallElement) expression; 821 return callExpression.getCalleeExpression(); 822 } 823 if (expression instanceof JetQualifiedExpression) { 824 JetExpression selectorExpression = ((JetQualifiedExpression) expression).getSelectorExpression(); 825 if (selectorExpression != null) { 826 return getCalleeExpressionIfAny(selectorExpression); 827 } 828 } 829 if (expression instanceof JetUnaryExpression) { 830 return ((JetUnaryExpression) expression).getOperationReference(); 831 } 832 if (expression instanceof JetBinaryExpression) { 833 return ((JetBinaryExpression) expression).getOperationReference(); 834 } 835 return null; 836 } 837 838 @Nullable 839 public static PsiElement skipSiblingsBackwardByPredicate(@Nullable PsiElement element, Predicate<PsiElement> elementsToSkip) { 840 if (element == null) return null; 841 for (PsiElement e = element.getPrevSibling(); e != null; e = e.getPrevSibling()) { 842 if (elementsToSkip.apply(e)) continue; 843 return e; 844 } 845 return null; 846 } 847 848 @Nullable 849 public static PsiElement skipSiblingsForwardByPredicate(@Nullable PsiElement element, Predicate<PsiElement> elementsToSkip) { 850 if (element == null) return null; 851 for (PsiElement e = element.getNextSibling(); e != null; e = e.getNextSibling()) { 852 if (elementsToSkip.apply(e)) continue; 853 return e; 854 } 855 return null; 856 } 857}