Skip to content

Commit

Permalink
Moved some functions from PsiAnalyzerUtil to the common module and pa…
Browse files Browse the repository at this point in the history
…rameterized tests
  • Loading branch information
anchouls committed Dec 13, 2023
1 parent 219755e commit 94dce6d
Show file tree
Hide file tree
Showing 10 changed files with 359 additions and 505 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package org.jetbrains.academy.test.system.ij.analyzer

import com.intellij.psi.PsiElement

/**
* Retrieves the constant value of the PsiElement.
*
* @param constantExpressionClass The classes of the constant expressions to search for.
* @return The constant value as a string, or null if no constant value is found.
* @throws IllegalArgumentException If the PsiElement contains multiple constant values.
*/
fun <T : PsiElement> PsiElement.getConstValue(vararg constantExpressionClass: Class<out T>): String? {
val possibleValue = extractElementsOfTypes(*constantExpressionClass)
if (possibleValue.isEmpty()) {
return null
}
require(possibleValue.size == 1) { "Parser error! A const variable must have only one value" }
return possibleValue.first().text.trimIndent()
}

/**
* Retrieves the body of a block represented by the given code block class.
*
* @param codeBlockClass the class representing the code block
* @return the body of the block as a string, or null if nobody is found
*/
fun <T : PsiElement> PsiElement.getBlockBody(codeBlockClass: Class<T>): String? {
val possibleBody = extractElementsOfTypes(codeBlockClass)
if (possibleBody.isEmpty()) {
return null
}
return possibleBody.first().text.trimBraces().trimIndent()
}

/**
* Trims leading and trailing braces from a string.
*/
private fun String.trimBraces() = dropWhile { it.isWhitespace() }.removePrefix("{")
.dropLastWhile { it.isWhitespace() }.removeSuffix("}")
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
package org.jetbrains.academy.test.system.ij.analyzer

import com.intellij.openapi.application.ApplicationManager
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiFile
import com.intellij.psi.PsiNamedElement
import com.intellij.psi.util.PsiTreeUtil
import com.intellij.psi.util.parentsOfType

/** Extracts elements of given type from related files in project. */
fun <T : PsiElement> PsiElement.extractElementsOfTypes(vararg psiElementClass: Class<out T>): MutableCollection<T> =
psiElementClass.flatMap { PsiTreeUtil.collectElementsOfType(this, it) }.toMutableList()

/**
* Checks if the PsiFile contains an element of the specified type and with the given name.
*
* @param T the type of the PsiNamedElement subclass to check for
* @param psiElementClass the class object representing the type of the element to check for
* @param name the name of the element to search for
* @return true if an element of the specified type and name exists in the PsiFile, false otherwise
*/
fun <T : PsiNamedElement> PsiFile.hasElementOfTypeWithName(psiElementClass: Class<out T>, name: String): Boolean =
ApplicationManager.getApplication().runReadAction<Boolean> {
extractElementsOfTypes(psiElementClass).any { it.name == name }
}

/**
* Retrieves the text of the parent element of the given element.
*
* @param element The element for which to retrieve the parent text.
* @param isParentTypeFunction True if the parent element's type is a function; false otherwise.
* @param parentClass The class of the parent element.
* @return The text of the parent element, or null if the parent does not exist or is not of the specified type.
*/
private fun <T : PsiNamedElement> getParentText(
element: PsiElement,
isParentTypeFunction: Boolean,
parentClass: Class<out T>
): String? {
return if (isParentTypeFunction) {
element.parentsOfType(parentClass).firstOrNull()?.name
} else {
element.parent?.text
}
}

/**
* Checks if the given PsiFile has an expression with the specified parent.
*
* @param expression The text of the expression to search for.
* @param parent The text of the parent to compare with.
* @param isParentTypeFunction Specifies if the parent should be treated as a function or not.
* @param parentClass The class of the parent element.
* @param expressionClass The classes of the expression elements to search for.
* @return true if the PsiFile has an expression with the specified parent, false otherwise.
*/
fun <T : PsiElement, V : PsiNamedElement> PsiFile.hasExpressionWithParent(
expression: String,
parent: String?,
isParentTypeFunction: Boolean,
parentClass: Class<out V>,
vararg expressionClass: Class<out T>
): Boolean =
ApplicationManager.getApplication().runReadAction<Boolean> {
val expressions: MutableCollection<PsiElement> = extractElementsOfTypes(*expressionClass)
expressions.any { it.text == expression && getParentText(it, isParentTypeFunction, parentClass) == parent }
}

/**
* Finds usages of a given method in the PSI file.
*
* @param methodName The name of the method to find usages of.
* @param callExpressionClass The class representing the call expression to search for.
* @param methodClass The class representing the method to search for usages of.
* @return A list of strings representing the names of the methods in which the given method is used.
*/
fun <T : PsiElement, V : PsiNamedElement> PsiFile.findMethodUsages(
methodName: String,
callExpressionClass: Class<T>,
methodClass: Class<V>
): List<String> =
ApplicationManager.getApplication().runReadAction<List<String>> {
val callExpression = extractElementsOfTypes(callExpressionClass)
callExpression.filter { it.text == methodName }.mapNotNull {
it.parentsOfType(methodClass).firstOrNull()?.name
}.toList()
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package org.jetbrains.academy.test.system.java.ij.analyzer

import com.intellij.ide.highlighter.JavaFileType
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.project.Project
import com.intellij.psi.PsiFile
import com.intellij.psi.PsiField
import com.intellij.psi.PsiMethod
import com.intellij.psi.PsiFileFactory
import com.intellij.psi.PsiLiteralExpression
import com.intellij.psi.PsiCodeBlock
import org.jetbrains.academy.test.system.ij.analyzer.extractElementsOfTypes
import org.jetbrains.academy.test.system.ij.analyzer.getBlockBody
import org.jetbrains.academy.test.system.ij.analyzer.getConstValue
import org.jetbrains.academy.test.system.ij.formatting.formatting

/**
* Checks if PsiFile contains a constant property with the given element value.
*
* @param elementValue The value to search for in the constant properties.
* @return true if a constant property with the specified value is found, false otherwise.
*/
fun PsiFile.hasConstantWithGivenValue(elementValue: String): Boolean =
ApplicationManager.getApplication().runReadAction<Boolean> {
val elements = extractElementsOfTypes(PsiField::class.java)
elements.any { it.modifierList?.text?.contains("final") ?: false && it.getConstValue(PsiLiteralExpression::class.java) == elementValue }
}

/**
* Formatting code content.
*
* @param content The source code to wrap and format.
* @param project An instance of the project, used for creating PsiFile.
* @return The formatted code content.
*/
private fun formattingContent(content: String, project: Project): String {
val wrappedCode = "class WrappedClass { void wrappedMethod() { $content } }"
val factory = PsiFileFactory.getInstance(project)
val contentPsiFile = factory.createFileFromText("Content.java", JavaFileType.INSTANCE, wrappedCode)
val formattingContent = contentPsiFile.formatting() ?: ""
return formattingContent.lines().drop(2).dropLast(2).joinToString(System.lineSeparator()).trimIndent()
}

/**
* Finds methods within the given PsiFile that have the specified body content.
*
* @param content The body content to search for in the methods.
* @return A list of method names whose bodies match the provided content.
*/
fun PsiFile.findMethodsWithContent(content: String): List<String> =
ApplicationManager.getApplication().runReadAction<List<String>> {
formatting()
val formattingContent = formattingContent(content, project)

val methods = extractElementsOfTypes(PsiMethod::class.java)
methods.filter { it.getBlockBody(PsiCodeBlock::class.java) == formattingContent }.mapNotNull { it.name }.toList()
}
Loading

0 comments on commit 94dce6d

Please sign in to comment.