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.google.common.base.Function;
020 import com.google.common.base.Predicate;
021 import com.google.common.collect.Lists;
022 import com.intellij.lang.ASTNode;
023 import com.intellij.psi.PsiComment;
024 import com.intellij.psi.PsiElement;
025 import com.intellij.psi.PsiFile;
026 import com.intellij.psi.PsiWhiteSpace;
027 import com.intellij.psi.impl.CheckUtil;
028 import com.intellij.psi.impl.source.codeStyle.CodeEditUtil;
029 import com.intellij.psi.tree.IElementType;
030 import com.intellij.psi.util.PsiTreeUtil;
031 import com.intellij.util.codeInsight.CommentUtilCore;
032 import com.intellij.util.containers.ContainerUtil;
033 import org.jetbrains.annotations.NotNull;
034 import org.jetbrains.annotations.Nullable;
035 import org.jetbrains.jet.JetNodeTypes;
036 import org.jetbrains.jet.kdoc.psi.api.KDocElement;
037 import org.jetbrains.jet.lang.parsing.JetExpressionParsing;
038 import org.jetbrains.jet.lang.resolve.ImportPath;
039 import org.jetbrains.jet.lang.resolve.name.FqName;
040 import org.jetbrains.jet.lang.resolve.name.Name;
041 import org.jetbrains.jet.lang.types.expressions.OperatorConventions;
042 import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns;
043 import org.jetbrains.jet.lexer.JetToken;
044 import org.jetbrains.jet.lexer.JetTokens;
045
046 import java.util.Collection;
047 import java.util.HashSet;
048 import java.util.List;
049 import java.util.Set;
050
051 public 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 JetElement getOutermostDescendantElement(
783 @Nullable PsiElement root,
784 boolean first,
785 final @NotNull Predicate<JetElement> predicate
786 ) {
787 if (!(root instanceof JetElement)) return null;
788
789 final List<JetElement> results = Lists.newArrayList();
790
791 ((JetElement) root).accept(
792 new JetVisitorVoid() {
793 @Override
794 public void visitJetElement(JetElement element) {
795 if (predicate.apply(element)) {
796 //noinspection unchecked
797 results.add(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 }