-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #22 from jetbrains-academy/intellij-java-module
Splitting intellij module into 3 modules: core, java-psi, kotlin-psi
- Loading branch information
Showing
27 changed files
with
917 additions
and
262 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
group = rootProject.group | ||
version = rootProject.version |
39 changes: 39 additions & 0 deletions
39
...n/src/main/kotlin/org/jetbrains/academy/test/system/ij/analyzer/PsiElementAnalyzerUtil.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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("}") |
87 changes: 87 additions & 0 deletions
87
...mmon/src/main/kotlin/org/jetbrains/academy/test/system/ij/analyzer/PsiFileAnalyzerUtil.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
group = rootProject.group | ||
version = rootProject.version | ||
|
||
dependencies { | ||
implementation(project(":ij:common")) | ||
} |
57 changes: 57 additions & 0 deletions
57
...src/main/kotlin/org/jetbrains/academy/test/system/java/ij/analyzer/PsiFileAnalyzerUtil.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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() | ||
} |
17 changes: 17 additions & 0 deletions
17
...src/main/kotlin/org/jetbrains/academy/test/system/java/ij/formatting/UnusedImportsUtil.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
package org.jetbrains.academy.test.system.java.ij.formatting | ||
|
||
import com.intellij.psi.PsiFile | ||
import com.intellij.openapi.application.ApplicationManager | ||
import com.intellij.openapi.command.WriteCommandAction | ||
import com.intellij.psi.codeStyle.JavaCodeStyleManager | ||
|
||
fun PsiFile.checkIfOptimizeImportsWereApplied() { | ||
val originalCode = ApplicationManager.getApplication().runReadAction<String> { text } | ||
WriteCommandAction.runWriteCommandAction(project) { | ||
JavaCodeStyleManager.getInstance(project).optimizeImports(this) | ||
} | ||
val optimizedCode = ApplicationManager.getApplication().runReadAction<String> { text } | ||
assert(originalCode.trimIndent() == optimizedCode.trimIndent()) { | ||
"Please, apply \"Optimize import\" option when formatting code." | ||
} | ||
} |
48 changes: 48 additions & 0 deletions
48
ij/java-psi/src/main/kotlin/org/jetbrains/academy/test/system/java/test/BaseIjTestClass.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
package org.jetbrains.academy.test.system.java.test | ||
|
||
import com.intellij.psi.PsiReferenceExpression | ||
import com.intellij.psi.PsiNewExpression | ||
import com.intellij.psi.PsiMethodCallExpression | ||
import com.intellij.psi.PsiParameter | ||
import com.intellij.psi.PsiClass | ||
import com.intellij.psi.PsiField | ||
import com.intellij.psi.PsiMethod | ||
import com.intellij.psi.PsiCallExpression | ||
import com.intellij.testFramework.fixtures.BasePlatformTestCase | ||
import org.jetbrains.academy.test.system.ij.analyzer.findMethodUsages | ||
import org.jetbrains.academy.test.system.ij.analyzer.hasElementOfTypeWithName | ||
import org.jetbrains.academy.test.system.ij.analyzer.hasExpressionWithParent | ||
import org.jetbrains.academy.test.system.java.ij.analyzer.findMethodsWithContent | ||
import org.jetbrains.academy.test.system.java.ij.analyzer.hasConstantWithGivenValue | ||
|
||
/** | ||
* A Base test class to create tests with PSI without adding intellij dependency into courses directly. | ||
*/ | ||
open class BaseIjTestClass : BasePlatformTestCase() { | ||
|
||
fun hasConstantWithGivenValue(elementValue: String): Boolean = | ||
myFixture.file.hasConstantWithGivenValue(elementValue) | ||
|
||
fun findMethodsWithContent(content: String): List<String> = | ||
myFixture.file.findMethodsWithContent(content) | ||
|
||
fun findMethodUsages(content: String): List<String> = | ||
myFixture.file.findMethodUsages(content, PsiCallExpression::class.java, PsiMethod::class.java) | ||
|
||
fun hasProperty(propertyName: String): Boolean = | ||
myFixture.file.hasElementOfTypeWithName(PsiField::class.java, propertyName) | ||
|
||
fun hasMethod(methodName: String): Boolean = | ||
myFixture.file.hasElementOfTypeWithName(PsiMethod::class.java, methodName) | ||
|
||
fun hasClass(className: String): Boolean = myFixture.file.hasElementOfTypeWithName(PsiClass::class.java, className) | ||
|
||
fun hasParameter(parameterName: String): Boolean = | ||
myFixture.file.hasElementOfTypeWithName(PsiParameter::class.java, parameterName) | ||
|
||
fun hasExpressionWithParent(expression: String, parent: String?, isParentTypeFunction: Boolean = false): Boolean = | ||
myFixture.file.hasExpressionWithParent( | ||
expression, parent, isParentTypeFunction, PsiMethod::class.java, | ||
PsiNewExpression::class.java, PsiReferenceExpression::class.java, PsiMethodCallExpression::class.java | ||
) | ||
} |
17 changes: 17 additions & 0 deletions
17
...-psi/src/test/kotlin/org/jetbrains/academy/test/system/java/ij/BaseFormattingUtilTests.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
package org.jetbrains.academy.test.system.java.ij | ||
|
||
import com.intellij.psi.PsiFile | ||
import com.intellij.testFramework.fixtures.BasePlatformTestCase | ||
import org.junit.Ignore | ||
|
||
@Ignore | ||
open class BaseFormattingUtilTests : BasePlatformTestCase() { | ||
|
||
protected fun testWithError(code: String, action: (PsiFile) -> Unit) { | ||
assertThrows(AssertionError::class.java) { action(myFixture.configureByText("dummy.java", code)) } | ||
} | ||
|
||
protected fun testWithoutError(code: String, action: (PsiFile) -> Unit) { | ||
action(myFixture.configureByText("dummy.java", code)) | ||
} | ||
} |
Oops, something went wrong.