mirror of
https://gitee.com/zhangsken/AndroidIDE.git
synced 2026-10-15 23:44:48 +08:00
fix: remove Realm DB
This commit is contained in:
Generated
+2
@@ -116,6 +116,7 @@
|
||||
<module name="AndroidIDE.colorschemes" target="11" />
|
||||
<module name="AndroidIDE.colorschemes.main" target="11" />
|
||||
<module name="AndroidIDE.common" target="17" />
|
||||
<module name="AndroidIDE.core.database" target="17" />
|
||||
<module name="AndroidIDE.core.di" target="17" />
|
||||
<module name="AndroidIDE.core.indexing" target="17" />
|
||||
<module name="AndroidIDE.editor-api" target="17" />
|
||||
@@ -272,6 +273,7 @@
|
||||
<module name="AndroidIDE.uidesigner" target="17" />
|
||||
<module name="AndroidIDE.xml-inflater" target="17" />
|
||||
<module name="AndroidIDE.xml.resources-api.test" target="11" />
|
||||
<module name="AndroidIDE.xml.resources-indexing" target="17" />
|
||||
<module name="ATS.android-tree-sitter" target="17" />
|
||||
<module name="ATS.app" target="17" />
|
||||
<module name="ATS.tree-sitter-aidl" target="17" />
|
||||
|
||||
Generated
+1
-1
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="KotlinJpsPluginSettings">
|
||||
<option name="version" value="1.9.22" />
|
||||
<option name="version" value="1.9.24" />
|
||||
</component>
|
||||
</project>
|
||||
@@ -42,7 +42,6 @@ buildscript {
|
||||
dependencies {
|
||||
classpath(libs.kotlin.gradle.plugin)
|
||||
classpath(libs.nav.safe.args.gradle.plugin)
|
||||
classpath(libs.realm.gradle.plugin)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -28,7 +28,6 @@ plugins {
|
||||
id("kotlin-android")
|
||||
id("kotlin-kapt")
|
||||
id("kotlin-parcelize")
|
||||
id("realm-android")
|
||||
id("androidx.navigation.safeargs.kotlin")
|
||||
id("com.itsaky.androidide.desugaring")
|
||||
}
|
||||
|
||||
@@ -58,7 +58,6 @@ import com.itsaky.androidide.utils.flashError
|
||||
import com.termux.app.TermuxApplication
|
||||
import com.termux.shared.reflection.ReflectionUtils
|
||||
import io.github.rosemoe.sora.widget.schemes.EditorColorScheme
|
||||
import io.realm.Realm
|
||||
import kotlinx.coroutines.DelicateCoroutinesApi
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.launch
|
||||
@@ -110,8 +109,6 @@ class IDEApplication : TermuxApplication() {
|
||||
|
||||
EventBus.getDefault().register(this)
|
||||
|
||||
Realm.init(this)
|
||||
|
||||
AppCompatDelegate.setDefaultNightMode(GeneralPreferences.uiMode)
|
||||
|
||||
if (IThemeManager.getInstance().getCurrentTheme() == IDETheme.MATERIAL_YOU) {
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
/build
|
||||
@@ -1,41 +0,0 @@
|
||||
/*
|
||||
* This file is part of AndroidIDE.
|
||||
*
|
||||
* AndroidIDE is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* AndroidIDE is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
import com.itsaky.androidide.build.config.BuildConfig
|
||||
|
||||
plugins {
|
||||
id("com.android.library")
|
||||
id("kotlin-android")
|
||||
id("kotlin-kapt")
|
||||
id("realm-android")
|
||||
}
|
||||
|
||||
|
||||
|
||||
android {
|
||||
namespace = "${BuildConfig.packageName}.database"
|
||||
}
|
||||
|
||||
dependencies {
|
||||
kapt(libs.google.auto.service)
|
||||
implementation(libs.google.auto.service.annotations)
|
||||
|
||||
implementation(projects.core.common)
|
||||
implementation(projects.logging.logger)
|
||||
implementation(projects.utilities.shared)
|
||||
}
|
||||
Vendored
-21
@@ -1,21 +0,0 @@
|
||||
# Add project specific ProGuard rules here.
|
||||
# You can control the set of applied configuration files using the
|
||||
# proguardFiles setting in build.gradle.
|
||||
#
|
||||
# For more details, see
|
||||
# http://developer.android.com/guide/developing/tools/proguard.html
|
||||
|
||||
# If your project uses WebView with JS, uncomment the following
|
||||
# and specify the fully qualified class name to the JavaScript interface
|
||||
# class:
|
||||
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
|
||||
# public *;
|
||||
#}
|
||||
|
||||
# Uncomment this to preserve the line number information for
|
||||
# debugging stack traces.
|
||||
#-keepattributes SourceFile,LineNumberTable
|
||||
|
||||
# If you keep the line number information, uncomment this to
|
||||
# hide the original source file name.
|
||||
#-renamesourcefileattribute SourceFile
|
||||
@@ -1,91 +0,0 @@
|
||||
/*
|
||||
* This file is part of AndroidIDE.
|
||||
*
|
||||
* AndroidIDE is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* AndroidIDE is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.itsaky.androidide.db
|
||||
|
||||
import com.itsaky.androidide.utils.ServiceLoader
|
||||
import io.realm.Realm
|
||||
import io.realm.RealmConfiguration
|
||||
|
||||
/**
|
||||
* An [IRealmProvider] provides the instance of [Realm].
|
||||
*
|
||||
* @author Akash Yadav
|
||||
*/
|
||||
interface IRealmProvider {
|
||||
|
||||
/**
|
||||
* Get the [Realm] instance.
|
||||
*
|
||||
* @param path The path of the database.
|
||||
*/
|
||||
fun get(path: String): Realm {
|
||||
return get(path, null)
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the [Realm] instance.
|
||||
*
|
||||
* @param path The path of the database.
|
||||
* @param config An optional function to configure the [RealmConfiguration] builder.
|
||||
*/
|
||||
fun get(path: String, config: (RealmConfiguration.Builder.() -> Unit)?): Realm
|
||||
|
||||
companion object {
|
||||
|
||||
private lateinit var sInstance: IRealmProvider
|
||||
|
||||
/**
|
||||
* The separator used to separate segments in an index path.
|
||||
*/
|
||||
const val PATH_SEPARATOR = '/'
|
||||
|
||||
/**
|
||||
* Create the path for the given segments.
|
||||
*
|
||||
* @param first The first (root) path segment.
|
||||
* @param rest The rest of the path segments.
|
||||
* @return The created path string.
|
||||
*/
|
||||
@JvmStatic
|
||||
fun createPath(first: String, vararg rest: String): String {
|
||||
|
||||
val sb = StringBuilder()
|
||||
sb.append(PATH_SEPARATOR)
|
||||
sb.append(first)
|
||||
|
||||
for (segment in rest) {
|
||||
sb.append(PATH_SEPARATOR)
|
||||
sb.append(segment)
|
||||
}
|
||||
|
||||
return sb.toString()
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the [IRealmProvider] instance, finding the implementation if necessary.
|
||||
*/
|
||||
@JvmStatic
|
||||
fun instance(): IRealmProvider {
|
||||
if (!Companion::sInstance.isInitialized) {
|
||||
val clazz = IRealmProvider::class.java
|
||||
sInstance = ServiceLoader.load(clazz, clazz.classLoader).findFirstOrThrow()
|
||||
}
|
||||
return sInstance
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,66 +0,0 @@
|
||||
/*
|
||||
* This file is part of AndroidIDE.
|
||||
*
|
||||
* AndroidIDE is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* AndroidIDE is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.itsaky.androidide.db.internal
|
||||
|
||||
import io.realm.RealmObject
|
||||
import io.realm.annotations.PrimaryKey
|
||||
import io.realm.annotations.RealmField
|
||||
import io.realm.annotations.Required
|
||||
|
||||
/**
|
||||
* An entity to store information about all registered box stores.
|
||||
*
|
||||
* @author Akash Yadav
|
||||
*/
|
||||
open class DatabaseEntity(
|
||||
@Required
|
||||
@PrimaryKey
|
||||
@RealmField("path")
|
||||
var path: String = "",
|
||||
|
||||
@Required
|
||||
@RealmField("name")
|
||||
var name: String = "",
|
||||
|
||||
@Required
|
||||
@RealmField("directory")
|
||||
var directory: String = "",
|
||||
) : RealmObject() {
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (other !is DatabaseEntity) return false
|
||||
|
||||
if (path != other.path) return false
|
||||
if (name != other.name) return false
|
||||
if (directory != other.directory) return false
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
var result = path.hashCode()
|
||||
result = 31 * result + name.hashCode()
|
||||
result = 31 * result + directory.hashCode()
|
||||
return result
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
return "DatabaseEntity(path='$path', name='$name', directory='$directory')"
|
||||
}
|
||||
}
|
||||
-28
@@ -1,28 +0,0 @@
|
||||
/*
|
||||
* This file is part of AndroidIDE.
|
||||
*
|
||||
* AndroidIDE is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* AndroidIDE is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.itsaky.androidide.db.internal
|
||||
|
||||
import io.realm.annotations.RealmModule
|
||||
|
||||
/**
|
||||
* [RealmModule] for the internal database entitites.
|
||||
*
|
||||
* @author Akash Yadav
|
||||
*/
|
||||
@RealmModule(library = true, classes = [DatabaseEntity::class])
|
||||
internal class InternalDBRealmModule
|
||||
@@ -1,115 +0,0 @@
|
||||
/*
|
||||
* This file is part of AndroidIDE.
|
||||
*
|
||||
* AndroidIDE is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* AndroidIDE is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.itsaky.androidide.db.internal
|
||||
|
||||
import com.google.auto.service.AutoService
|
||||
import com.itsaky.androidide.db.IRealmProvider
|
||||
import com.itsaky.androidide.utils.Environment
|
||||
import io.realm.Realm
|
||||
import io.realm.RealmConfiguration
|
||||
import org.slf4j.LoggerFactory
|
||||
|
||||
/**
|
||||
* Default implementation of [IRealmProvider].
|
||||
*
|
||||
* @author Akash Yadav
|
||||
*/
|
||||
@AutoService(IRealmProvider::class)
|
||||
@Suppress("unused")
|
||||
internal class RealmProviderImpl : IRealmProvider {
|
||||
|
||||
companion object {
|
||||
private val log = LoggerFactory.getLogger(RealmProviderImpl::class.java)
|
||||
|
||||
// must be a relative path, with no special characters except '_' '-' and '/' (path sep)
|
||||
private val dbNameRegex = Regex("^[a-zA-Z0-9_\\-/]+$")
|
||||
private const val MASTER_DB_NAME = "master"
|
||||
private const val MASTER_DB_PATH = "/$MASTER_DB_NAME"
|
||||
}
|
||||
|
||||
private val masterDb: Realm by lazy {
|
||||
createDb(MASTER_DB_PATH) {
|
||||
modules(InternalDBRealmModule())
|
||||
}
|
||||
}
|
||||
|
||||
override fun get(path: String, config: (RealmConfiguration.Builder.() -> Unit)?): Realm {
|
||||
require(path != MASTER_DB_PATH) {
|
||||
"Master DB is internal to AndroidIDE"
|
||||
}
|
||||
|
||||
return createDb(path, config)
|
||||
}
|
||||
|
||||
private fun createDb(path: String, confFunc: (RealmConfiguration.Builder.() -> Unit)?): Realm {
|
||||
val (parentPath, name) = validateDbPath(path)
|
||||
|
||||
val configBuilder = RealmConfiguration.Builder()
|
||||
confFunc?.also { configure -> configBuilder.configure() }
|
||||
|
||||
var dbDir = Environment.REALM_DB_DIR
|
||||
if (parentPath.isNotEmpty()) {
|
||||
dbDir = dbDir.resolve(parentPath)
|
||||
}
|
||||
|
||||
val dbName = "${name}.realm"
|
||||
val config = configBuilder
|
||||
.name(dbName)
|
||||
.directory(dbDir)
|
||||
.allowQueriesOnUiThread(false)
|
||||
.allowWritesOnUiThread(false)
|
||||
.compactOnLaunch()
|
||||
.build()
|
||||
|
||||
log.info("Creating DB at $dbDir/$dbName")
|
||||
|
||||
if (parentPath.isEmpty() && name != MASTER_DB_NAME) {
|
||||
masterDb.executeTransactionAsync { realm ->
|
||||
realm.insertOrUpdate(DatabaseEntity(name = name, path = path, directory = dbDir.absolutePath))
|
||||
}
|
||||
}
|
||||
|
||||
return Realm.getInstance(config)
|
||||
}
|
||||
|
||||
private fun validateDbPath(path: String): Pair<String, String> {
|
||||
val idx = path.indexOfLast { it == IRealmProvider.PATH_SEPARATOR }
|
||||
require(
|
||||
path.matches(dbNameRegex)
|
||||
&& path[0] == IRealmProvider.PATH_SEPARATOR
|
||||
&& idx >= 0
|
||||
&& path.indexOf("//") == -1
|
||||
) {
|
||||
"Invalid DB path: $path"
|
||||
}
|
||||
|
||||
val parentPath = if (idx > 1) {
|
||||
path.substring(1, idx)
|
||||
} else ""
|
||||
|
||||
val name = path.substring(idx + 1)
|
||||
|
||||
if (name == MASTER_DB_NAME) {
|
||||
require(parentPath.isEmpty()) {
|
||||
"Master DB must not have any parent path"
|
||||
}
|
||||
}
|
||||
|
||||
return parentPath to name
|
||||
}
|
||||
}
|
||||
@@ -1,60 +0,0 @@
|
||||
/*
|
||||
* This file is part of AndroidIDE.
|
||||
*
|
||||
* AndroidIDE is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* AndroidIDE is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.itsaky.androidide.db.utils
|
||||
|
||||
import io.realm.RealmDictionary
|
||||
|
||||
/**
|
||||
* Check if two RealmDictionaries are equal.
|
||||
*/
|
||||
fun <T> RealmDictionary<out T>?.isEqualTo(other: Any?): Boolean {
|
||||
// implementation taken from java/util/AbstractMap from JDK 21
|
||||
if (other == this) return true
|
||||
if (other == null || this == null) return false
|
||||
if (other !is RealmDictionary<*>) return false
|
||||
if (this.size != other.size) return false
|
||||
|
||||
try {
|
||||
for (entry in this.entries) {
|
||||
val key = entry.key
|
||||
val value = entry.value
|
||||
if (value == null) {
|
||||
if (!(other[key] == null && other.containsKey(key))) {
|
||||
return false
|
||||
}
|
||||
} else {
|
||||
if (value != other[key]) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (e: ClassCastException) {
|
||||
return false
|
||||
} catch (e: NullPointerException) {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate the hash code for a RealmDictionary.
|
||||
*/
|
||||
fun <T> RealmDictionary<T>.hash(): Int {
|
||||
return this.entries.sumOf { it.hashCode() }
|
||||
}
|
||||
@@ -22,11 +22,8 @@ plugins {
|
||||
id("com.android.library")
|
||||
id("kotlin-android")
|
||||
id("kotlin-kapt")
|
||||
id("realm-android")
|
||||
}
|
||||
|
||||
|
||||
|
||||
android {
|
||||
namespace = "${BuildConfig.packageName}.indexing.api"
|
||||
}
|
||||
@@ -36,7 +33,6 @@ dependencies {
|
||||
implementation(libs.google.auto.service.annotations)
|
||||
|
||||
api(projects.core.common)
|
||||
api(projects.core.database)
|
||||
api(projects.core.projects)
|
||||
api(projects.logging.logger)
|
||||
api(projects.utilities.shared)
|
||||
|
||||
@@ -1,59 +0,0 @@
|
||||
/*
|
||||
* This file is part of AndroidIDE.
|
||||
*
|
||||
* AndroidIDE is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* AndroidIDE is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.itsaky.androidide.indexing
|
||||
|
||||
import io.realm.Realm
|
||||
|
||||
/**
|
||||
* Abstract implementation of [IIndex] for indexing data in the database.
|
||||
*
|
||||
* @property params The index parameters.
|
||||
* @author Akash Yadav
|
||||
*/
|
||||
abstract class AbstractDBIndex<T : IIndexable>(
|
||||
protected val params: IIndexParams?
|
||||
) : IDatabaseIndex<T> {
|
||||
|
||||
protected abstract val realm: Realm
|
||||
|
||||
override fun index(symbol: T) {
|
||||
realm.executeTransaction {
|
||||
it.insertOrUpdate(symbol)
|
||||
}
|
||||
}
|
||||
|
||||
override fun indexAsync(symbol: T) {
|
||||
realm.executeTransactionAsync {
|
||||
it.insertOrUpdate(symbol)
|
||||
}
|
||||
}
|
||||
|
||||
override fun indexAll(symbols: Collection<T>) {
|
||||
realm.executeTransaction {
|
||||
it.insertOrUpdate(symbols)
|
||||
}
|
||||
}
|
||||
|
||||
override fun indexAllAsync(symbols: Collection<T>) {
|
||||
realm.executeTransactionAsync { it.insertOrUpdate(symbols) }
|
||||
}
|
||||
|
||||
override fun delete() {
|
||||
realm.deleteAll()
|
||||
}
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
/*
|
||||
* This file is part of AndroidIDE.
|
||||
*
|
||||
* AndroidIDE is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* AndroidIDE is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.itsaky.androidide.indexing
|
||||
|
||||
import io.realm.RealmQuery
|
||||
|
||||
/**
|
||||
* An index which is stored in the database.
|
||||
*
|
||||
* @author Akash Yadav
|
||||
*/
|
||||
interface IDatabaseIndex<IndexableT : IIndexable> : IIndex<IndexableT> {
|
||||
|
||||
/**
|
||||
* Perform a query on the Realm database.
|
||||
*/
|
||||
fun query(): RealmQuery<IndexableT>
|
||||
}
|
||||
@@ -17,8 +17,6 @@
|
||||
|
||||
package com.itsaky.androidide.indexing
|
||||
|
||||
import com.itsaky.androidide.db.IRealmProvider
|
||||
|
||||
/**
|
||||
* An index of symbols of type [IndexableT].
|
||||
*
|
||||
@@ -28,6 +26,8 @@ interface IIndex<IndexableT : IIndexable> {
|
||||
|
||||
companion object {
|
||||
|
||||
const val PATH_SEPARATOR = '/'
|
||||
|
||||
/**
|
||||
* The default batch size for indexing.
|
||||
*/
|
||||
@@ -44,7 +44,7 @@ interface IIndex<IndexableT : IIndexable> {
|
||||
* Create an index path.
|
||||
*/
|
||||
fun createIndexPath(parentPath: String, name: String): String =
|
||||
"${parentPath}${IRealmProvider.PATH_SEPARATOR}${name}"
|
||||
"${parentPath}${PATH_SEPARATOR}${name}"
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -17,11 +17,9 @@
|
||||
|
||||
package com.itsaky.androidide.indexing
|
||||
|
||||
import io.realm.RealmModel
|
||||
|
||||
/**
|
||||
* An indexable object.
|
||||
*
|
||||
* @author Akash Yadav
|
||||
*/
|
||||
interface IIndexable : RealmModel
|
||||
interface IIndexable
|
||||
@@ -22,11 +22,8 @@ plugins {
|
||||
id("com.android.library")
|
||||
id("kotlin-android")
|
||||
id("kotlin-kapt")
|
||||
id("realm-android")
|
||||
}
|
||||
|
||||
|
||||
|
||||
android {
|
||||
namespace = "${BuildConfig.packageName}.indexing.core"
|
||||
}
|
||||
|
||||
-26
@@ -1,26 +0,0 @@
|
||||
/*
|
||||
* This file is part of AndroidIDE.
|
||||
*
|
||||
* AndroidIDE is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* AndroidIDE is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.itsaky.androidide.indexing.core
|
||||
|
||||
import io.realm.annotations.RealmModule
|
||||
|
||||
/**
|
||||
* @author Akash Yadav
|
||||
*/
|
||||
@RealmModule(classes = [])
|
||||
class IndexingCoreRealmModule
|
||||
+1
-1
@@ -27,7 +27,7 @@ import android.widget.TextView
|
||||
import com.itsaky.androidide.editor.R
|
||||
import com.itsaky.androidide.editor.databinding.LayoutCompletionItemBinding
|
||||
import com.itsaky.androidide.lookup.Lookup
|
||||
import com.itsaky.androidide.lsp.java.indexing.classfile.JavaType
|
||||
import com.itsaky.androidide.lsp.java.utils.JavaType
|
||||
import com.itsaky.androidide.lsp.models.ClassCompletionData
|
||||
import com.itsaky.androidide.lsp.models.CompletionItemKind.CLASS
|
||||
import com.itsaky.androidide.lsp.models.CompletionItemKind.CONSTRUCTOR
|
||||
|
||||
@@ -161,8 +161,6 @@ tooling-builderModel = { module = "com.android.tools.build:builder-model", versi
|
||||
tooling-gradleApi = { module = "com.itsaky.androidide.gradle:gradle-tooling-api", version.ref = "gradle-tooling" }
|
||||
tooling-slf4j = { module = "org.slf4j:slf4j-api", version = "2.0.12" }
|
||||
|
||||
realm-gradle-plugin = { module = "io.realm:realm-gradle-plugin", version.ref = "realm" }
|
||||
|
||||
# Classpaths
|
||||
android-gradle-plugin = { module = "com.android.tools.build:gradle", version.ref = "agp" }
|
||||
kotlin-gradle-plugin = { module = "org.jetbrains.kotlin:kotlin-gradle-plugin", version.ref = "kotlin" }
|
||||
|
||||
@@ -22,11 +22,8 @@ plugins {
|
||||
id("com.android.library")
|
||||
id("kotlin-android")
|
||||
id("kotlin-kapt")
|
||||
id("realm-android")
|
||||
}
|
||||
|
||||
|
||||
|
||||
android {
|
||||
namespace = "${BuildConfig.packageName}.lsp.java"
|
||||
|
||||
|
||||
+1
-1
@@ -20,7 +20,7 @@ package com.itsaky.androidide.lsp.java.indexing
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import com.itsaky.androidide.lsp.java.indexing.classfile.JavaClass
|
||||
import com.itsaky.androidide.lsp.java.indexing.classfile.JavaType
|
||||
import com.itsaky.androidide.lsp.java.utils.JavaType
|
||||
import com.itsaky.androidide.testing.android.rules.RealmDBTestRule
|
||||
import io.realm.RealmList
|
||||
import io.realm.RealmObject
|
||||
|
||||
+1
-1
@@ -20,7 +20,7 @@ package com.itsaky.androidide.lsp.java.indexing
|
||||
import com.itsaky.androidide.lsp.java.indexing.classfile.JavaClass
|
||||
import com.itsaky.androidide.lsp.java.indexing.classfile.JavaField
|
||||
import com.itsaky.androidide.lsp.java.indexing.classfile.JavaMethod
|
||||
import com.itsaky.androidide.lsp.java.indexing.classfile.JavaType
|
||||
import com.itsaky.androidide.lsp.java.utils.JavaType
|
||||
import io.realm.RealmList
|
||||
import openjdk.tools.classfile.AccessFlags
|
||||
|
||||
|
||||
+1
-1
@@ -25,7 +25,7 @@ import com.itsaky.androidide.lsp.java.indexing.classfile.ClassAnnotationElementV
|
||||
import com.itsaky.androidide.lsp.java.indexing.classfile.EnumAnnotationElementValue
|
||||
import com.itsaky.androidide.lsp.java.indexing.classfile.IAnnotationElementValue
|
||||
import com.itsaky.androidide.lsp.java.indexing.classfile.JavaConstant
|
||||
import com.itsaky.androidide.lsp.java.indexing.classfile.JavaType
|
||||
import com.itsaky.androidide.lsp.java.utils.JavaType
|
||||
import com.itsaky.androidide.lsp.java.indexing.classfile.PrimitiveAnnotationElementValue
|
||||
import com.itsaky.androidide.testing.android.rules.RealmDBTestRule
|
||||
import io.realm.RealmAny
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
/*
|
||||
* This file is part of AndroidIDE.
|
||||
*
|
||||
* AndroidIDE is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* AndroidIDE is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.itsaky.androidide.lsp.java.indexing
|
||||
|
||||
import com.itsaky.androidide.indexing.IIndexable
|
||||
|
||||
/**
|
||||
* @author Akash Yadav
|
||||
*/
|
||||
interface IJavaIndexable : IIndexable
|
||||
@@ -1,29 +0,0 @@
|
||||
/*
|
||||
* This file is part of AndroidIDE.
|
||||
*
|
||||
* AndroidIDE is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* AndroidIDE is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.itsaky.androidide.lsp.java.indexing
|
||||
|
||||
/**
|
||||
* A java symbol.
|
||||
*
|
||||
* @property accessFlags Access flags of the symbol.
|
||||
* @property apiInfo Info about the APIs in which this symbol was added, deprecated or removed in.
|
||||
* @author Akash Yadav
|
||||
*/
|
||||
interface IJavaSymbol : IJavaIndexable {
|
||||
var accessFlags: Int
|
||||
}
|
||||
@@ -1,73 +0,0 @@
|
||||
/*
|
||||
* This file is part of AndroidIDE.
|
||||
*
|
||||
* AndroidIDE is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* AndroidIDE is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.itsaky.androidide.lsp.java.indexing
|
||||
|
||||
import io.realm.RealmList
|
||||
import io.realm.annotations.Required
|
||||
|
||||
/**
|
||||
* A Java user type. May be a class, interface, enum, or annotation. Qualified name of a type or a
|
||||
* package name is always represented in the (partially) internal form. For example, the fully qualified
|
||||
* of `java.lang.Object` is `java/lang/Object` and the package name is `java/lang`.
|
||||
*
|
||||
* @property fqn The fully qualified name of the type.
|
||||
* @property name The simple name of the type.
|
||||
* @property packageName The package name of the type.
|
||||
* @property superInterfacesFqn The fully qualified names of the interfaces implemented by the type.
|
||||
* @property fields The fields of the type.
|
||||
* @property methods The methods of the type.
|
||||
* @property isClass Whether the type is a class.
|
||||
* @property isInterface Whether the type is an interface.
|
||||
* @property isAnnotation Whether the type is an annotation.
|
||||
* @property isEnum Whether the type is an enum.
|
||||
* @author Akash Yadav
|
||||
*/
|
||||
interface IJavaType<FieldT : IJavaSymbol, MethodT : IJavaSymbol> : IJavaSymbol {
|
||||
var fqn: String?
|
||||
var name: String?
|
||||
var packageName: String?
|
||||
var isInner: Boolean
|
||||
|
||||
/**
|
||||
* The fully qualified name of the superclass of the type.
|
||||
*
|
||||
* Info about fields and methods are availabe in the class file itself. Hhowever, a class may
|
||||
* extend a class (or implement an interface) which may be defined in other jar files. As a result,
|
||||
* we only store the fully qualifed names of the classes and interfaces then add an index based on
|
||||
* these fully qualifed names in the database.
|
||||
*
|
||||
* This property is not [Required] because we also index `java/lang/Object`, which does not have
|
||||
* a superclass.
|
||||
*/
|
||||
var superClassFqn: String?
|
||||
var superInterfacesFqn: RealmList<String>?
|
||||
var fields: RealmList<FieldT>?
|
||||
var methods: RealmList<MethodT>?
|
||||
|
||||
val isClass: Boolean
|
||||
get() = false
|
||||
|
||||
val isInterface: Boolean
|
||||
get() = false
|
||||
|
||||
val isAnnotation: Boolean
|
||||
get() = false
|
||||
|
||||
val isEnum: Boolean
|
||||
get() = false
|
||||
}
|
||||
-36
@@ -1,36 +0,0 @@
|
||||
/*
|
||||
* This file is part of AndroidIDE.
|
||||
*
|
||||
* AndroidIDE is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* AndroidIDE is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.itsaky.androidide.lsp.java.indexing
|
||||
|
||||
/**
|
||||
* An [IJavaIndexable] model which is shared between other models.
|
||||
*
|
||||
* @author Akash Yadav
|
||||
*/
|
||||
interface ISharedJavaIndexable : IJavaIndexable {
|
||||
|
||||
/**
|
||||
* A unique ID for the indexable.
|
||||
*/
|
||||
var id: Int?
|
||||
|
||||
/**
|
||||
* Compute and update the ID of this indexable.
|
||||
*/
|
||||
fun computeId()
|
||||
}
|
||||
-59
@@ -1,59 +0,0 @@
|
||||
/*
|
||||
* This file is part of AndroidIDE.
|
||||
*
|
||||
* AndroidIDE is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* AndroidIDE is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.itsaky.androidide.lsp.java.indexing
|
||||
|
||||
import com.itsaky.androidide.lsp.java.indexing.classfile.AnnotationAnnotationElementValue
|
||||
import com.itsaky.androidide.lsp.java.indexing.classfile.AnnotationElement
|
||||
import com.itsaky.androidide.lsp.java.indexing.classfile.ArrayAnnotationElementValue
|
||||
import com.itsaky.androidide.lsp.java.indexing.classfile.ClassAnnotationElementValue
|
||||
import com.itsaky.androidide.lsp.java.indexing.classfile.EnumAnnotationElementValue
|
||||
import com.itsaky.androidide.lsp.java.indexing.classfile.JavaAnnotation
|
||||
import com.itsaky.androidide.lsp.java.indexing.classfile.JavaClass
|
||||
import com.itsaky.androidide.lsp.java.indexing.classfile.JavaConstant
|
||||
import com.itsaky.androidide.lsp.java.indexing.classfile.JavaEnum
|
||||
import com.itsaky.androidide.lsp.java.indexing.classfile.JavaField
|
||||
import com.itsaky.androidide.lsp.java.indexing.classfile.JavaInterface
|
||||
import com.itsaky.androidide.lsp.java.indexing.classfile.JavaMethod
|
||||
import com.itsaky.androidide.lsp.java.indexing.classfile.JavaType
|
||||
import com.itsaky.androidide.lsp.java.indexing.classfile.PrimitiveAnnotationElementValue
|
||||
import io.realm.annotations.RealmModule
|
||||
|
||||
/**
|
||||
* [RealmModule] for the indexing databased for Java.
|
||||
*
|
||||
* @author Akash Yadav
|
||||
*/
|
||||
@RealmModule(
|
||||
library = true, classes = [
|
||||
AnnotationElement::class,
|
||||
AnnotationAnnotationElementValue::class,
|
||||
ArrayAnnotationElementValue::class,
|
||||
ClassAnnotationElementValue::class,
|
||||
EnumAnnotationElementValue::class,
|
||||
PrimitiveAnnotationElementValue::class,
|
||||
JavaAnnotation::class,
|
||||
JavaClass::class,
|
||||
JavaConstant::class,
|
||||
JavaEnum::class,
|
||||
JavaField::class,
|
||||
JavaInterface::class,
|
||||
JavaMethod::class,
|
||||
JavaType::class,
|
||||
]
|
||||
)
|
||||
internal class JavaIndexingRealmModule
|
||||
-424
@@ -1,424 +0,0 @@
|
||||
/*
|
||||
* This file is part of AndroidIDE.
|
||||
*
|
||||
* AndroidIDE is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* AndroidIDE is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.itsaky.androidide.lsp.java.indexing
|
||||
|
||||
import androidx.annotation.VisibleForTesting
|
||||
import com.itsaky.androidide.lsp.java.indexing.classfile.AnnotationAnnotationElementValue
|
||||
import com.itsaky.androidide.lsp.java.indexing.classfile.AnnotationElement
|
||||
import com.itsaky.androidide.lsp.java.indexing.classfile.AnnotationElement.Companion.newAnnotationElement
|
||||
import com.itsaky.androidide.lsp.java.indexing.classfile.ArrayAnnotationElementValue
|
||||
import com.itsaky.androidide.lsp.java.indexing.classfile.ClassAnnotationElementValue
|
||||
import com.itsaky.androidide.lsp.java.indexing.classfile.EnumAnnotationElementValue
|
||||
import com.itsaky.androidide.lsp.java.indexing.classfile.IAnnotationElementValue
|
||||
import com.itsaky.androidide.lsp.java.indexing.classfile.JavaAnnotation
|
||||
import com.itsaky.androidide.lsp.java.indexing.classfile.JavaClass
|
||||
import com.itsaky.androidide.lsp.java.indexing.classfile.JavaConstant
|
||||
import com.itsaky.androidide.lsp.java.indexing.classfile.JavaEnum
|
||||
import com.itsaky.androidide.lsp.java.indexing.classfile.JavaField
|
||||
import com.itsaky.androidide.lsp.java.indexing.classfile.JavaInterface
|
||||
import com.itsaky.androidide.lsp.java.indexing.classfile.JavaMethod
|
||||
import com.itsaky.androidide.lsp.java.indexing.classfile.JavaType
|
||||
import com.itsaky.androidide.lsp.java.indexing.classfile.PrimitiveAnnotationElementValue
|
||||
import com.itsaky.androidide.lsp.java.utils.DescriptorUtils
|
||||
import io.realm.RealmAny
|
||||
import io.realm.RealmDictionary
|
||||
import io.realm.RealmList
|
||||
import openjdk.tools.classfile.AccessFlags
|
||||
import openjdk.tools.classfile.Annotation.Annotation_element_value
|
||||
import openjdk.tools.classfile.Annotation.Array_element_value
|
||||
import openjdk.tools.classfile.Annotation.Class_element_value
|
||||
import openjdk.tools.classfile.Annotation.Enum_element_value
|
||||
import openjdk.tools.classfile.Annotation.Primitive_element_value
|
||||
import openjdk.tools.classfile.Annotation.element_value
|
||||
import openjdk.tools.classfile.AnnotationDefault_attribute
|
||||
import openjdk.tools.classfile.Attribute
|
||||
import openjdk.tools.classfile.ClassFile
|
||||
import openjdk.tools.classfile.ConstantPool
|
||||
import openjdk.tools.classfile.ConstantPool.CONSTANT_Double_info
|
||||
import openjdk.tools.classfile.ConstantPool.CONSTANT_Float_info
|
||||
import openjdk.tools.classfile.ConstantPool.CONSTANT_Integer_info
|
||||
import openjdk.tools.classfile.ConstantPool.CONSTANT_Long_info
|
||||
import openjdk.tools.classfile.ConstantPool.CONSTANT_String_info
|
||||
import openjdk.tools.classfile.ConstantPool.CONSTANT_Utf8_info
|
||||
import openjdk.tools.classfile.ConstantPool.CPInfo
|
||||
import openjdk.tools.classfile.ConstantValue_attribute
|
||||
import java.io.File
|
||||
import java.util.zip.ZipFile
|
||||
|
||||
/**
|
||||
* [JavaJarModelBuilder] builds models for Java classes in a JAR file.
|
||||
*
|
||||
* @author Akash Yadav
|
||||
*/
|
||||
class JavaJarModelBuilder(private val jar: File) {
|
||||
|
||||
// for testing purposes
|
||||
// prefer using consumeTypes(Function)
|
||||
@VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
|
||||
fun buildTypes(): List<IJavaType<*, *>> {
|
||||
val types = mutableListOf<IJavaType<*, *>>()
|
||||
consumeTypes { types.add(it) }
|
||||
return types
|
||||
}
|
||||
|
||||
fun consumeTypes(consumer: (IJavaType<*, *>) -> Unit) {
|
||||
ZipFile(jar).use { jar ->
|
||||
jar.entries().asSequence().filter { it.name.endsWith(".class") }.forEach { entry ->
|
||||
jar.getInputStream(entry).buffered().use { inputStream ->
|
||||
val classFile = ClassFile.read(inputStream)!!
|
||||
|
||||
// build model only if the class is not an anonymous or local class
|
||||
if (classFile.getAttribute(Attribute.EnclosingMethod) == null) {
|
||||
val type = buildType(classFile)
|
||||
consumer(type)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun buildType(classFile: ClassFile): IJavaType<*, *> {
|
||||
val classInfo = classFile.constant_pool.getClassInfo(classFile.this_class)
|
||||
val fqn = classInfo.baseName
|
||||
val (pck, name, isInner) = splitFqn(fqn)
|
||||
return buildType(fqn, pck, name, isInner, classFile)
|
||||
}
|
||||
|
||||
/**
|
||||
* Split the given fully qualified name of a class and return the package name and the simple name
|
||||
* of the class, along with whether the class is an inner class.
|
||||
*/
|
||||
private fun splitFqn(fqn: String): Triple<String, String, Boolean> {
|
||||
val dolIdx = fqn.lastIndexOf('$')
|
||||
|
||||
// start looking for '/' from the index of the last '$'
|
||||
// a '/' will always appear before a '$'
|
||||
val slaIdx = fqn.lastIndexOf('/', startIndex = if (dolIdx == -1) fqn.lastIndex else dolIdx - 1)
|
||||
|
||||
if (slaIdx == -1 && dolIdx != -1) {
|
||||
// fqn == SomeClass$InnerClass (inner class of a class in the root package)
|
||||
// pck is empty, but fqn is not the class's simple name
|
||||
return Triple("", fqn.substring(dolIdx + 1), true)
|
||||
}
|
||||
|
||||
if (slaIdx == -1) {
|
||||
// fqn == SomeClass (class in the root package)
|
||||
// pck is empty and fqn is class's simple name
|
||||
return Triple("", fqn, false)
|
||||
}
|
||||
|
||||
if (dolIdx == -1) {
|
||||
// fqn == com/example/SomeClass
|
||||
return Triple(fqn.substring(0, slaIdx), fqn.substring(slaIdx + 1), false)
|
||||
}
|
||||
|
||||
// fqn == com/example/SomeClass$InnerClass
|
||||
return Triple(fqn.substring(0, slaIdx), fqn.substring(dolIdx + 1), true)
|
||||
}
|
||||
|
||||
private fun buildType(
|
||||
fqn: String,
|
||||
pck: String,
|
||||
name: String,
|
||||
isInner: Boolean,
|
||||
classFile: ClassFile
|
||||
): IJavaType<*, *> {
|
||||
return when {
|
||||
classFile.access_flags.`is`(AccessFlags.ACC_ENUM) -> buildEnum(fqn, name, pck, classFile)
|
||||
classFile.access_flags.`is`(AccessFlags.ACC_ANNOTATION) -> buildAnnotation(
|
||||
fqn,
|
||||
name,
|
||||
pck,
|
||||
classFile
|
||||
)
|
||||
|
||||
classFile.access_flags.`is`(AccessFlags.ACC_INTERFACE) -> buildInterface(
|
||||
fqn,
|
||||
name,
|
||||
pck,
|
||||
classFile
|
||||
)
|
||||
|
||||
else -> buildClass(fqn, name, pck, classFile)
|
||||
}.also {
|
||||
it.isInner = isInner
|
||||
}
|
||||
}
|
||||
|
||||
private fun buildClass(fqn: String, name: String, pck: String, classFile: ClassFile): JavaClass {
|
||||
return JavaClass.newInstance(fqn, name, pck, classFile.access_flags.flags).apply {
|
||||
setSuperClass(classFile)
|
||||
setSuperInterfaces(classFile)
|
||||
setFields(classFile)
|
||||
setMethods(classFile)
|
||||
}
|
||||
}
|
||||
|
||||
private fun buildInterface(
|
||||
fqn: String, name: String, pck: String, classFile: ClassFile
|
||||
): JavaInterface {
|
||||
return JavaInterface.newInstance(fqn, name, pck, classFile.access_flags.flags).apply {
|
||||
setSuperClass(classFile)
|
||||
setSuperInterfaces(classFile)
|
||||
setFields(classFile)
|
||||
setMethods(classFile)
|
||||
}
|
||||
}
|
||||
|
||||
private fun buildAnnotation(
|
||||
fqn: String, name: String, pck: String, classFile: ClassFile
|
||||
): JavaAnnotation {
|
||||
return JavaAnnotation.newInstance(fqn, name, pck, classFile.access_flags.flags).apply {
|
||||
setSuperClass(classFile)
|
||||
setSuperInterfaces(classFile)
|
||||
setFields(classFile)
|
||||
setAnnotationElements(classFile)
|
||||
}
|
||||
}
|
||||
|
||||
private fun buildEnum(fqn: String, name: String, pck: String, classFile: ClassFile): JavaEnum {
|
||||
return JavaEnum.newInstance(fqn, name, pck, classFile.access_flags.flags).apply {
|
||||
setSuperClass(classFile)
|
||||
setSuperInterfaces(classFile)
|
||||
setFields(classFile)
|
||||
setMethods(classFile)
|
||||
}
|
||||
}
|
||||
|
||||
private fun IJavaType<*, *>.setSuperInterfaces(classFile: ClassFile) {
|
||||
val interfaces = RealmList<String>()
|
||||
for (iface in classFile.interfaces) {
|
||||
val ifaceInfo = classFile.constant_pool.getClassInfo(iface)
|
||||
interfaces.add(ifaceInfo.baseName!!)
|
||||
}
|
||||
|
||||
superInterfacesFqn = interfaces
|
||||
}
|
||||
|
||||
private fun IJavaType<*, *>.setSuperClass(classFile: ClassFile) {
|
||||
if (classFile.super_class == 0) {
|
||||
// From https://docs.oracle.com/javase/specs/jvms/se21/html/jvms-4.html#jvms-4.5
|
||||
// If the value of the super_class field is 0, then this class represents the java/lang/Object class itself
|
||||
// and hence, doesn't have a super class.
|
||||
return
|
||||
}
|
||||
|
||||
val superclassInfo = classFile.constant_pool.getClassInfo(classFile.super_class)
|
||||
superClassFqn = superclassInfo.baseName!!
|
||||
}
|
||||
|
||||
private fun IJavaType<*, JavaMethod>.setMethods(classFile: ClassFile) {
|
||||
methods = RealmList<JavaMethod>().apply {
|
||||
classFile.methods.forEach { method ->
|
||||
val descriptor = classFile.constant_pool.getUTF8Value(method.descriptor.index)
|
||||
val methodName = method.getName(classFile.constant_pool)
|
||||
val methodType = DescriptorUtils.returnType(descriptor)
|
||||
val paramTypes = RealmList<JavaType>().apply {
|
||||
addAll(DescriptorUtils.paramTypes(descriptor))
|
||||
}
|
||||
|
||||
add(JavaMethod.newInstance(methodName, paramTypes, methodType, method.access_flags.flags))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun IJavaType<JavaField, *>.setFields(classFile: ClassFile) {
|
||||
fields = RealmList<JavaField>().apply {
|
||||
classFile.fields.forEach { field ->
|
||||
val descriptor = classFile.constant_pool.getUTF8Value(field.descriptor.index)
|
||||
val fieldName = field.getName(classFile.constant_pool)
|
||||
val fieldType = DescriptorUtils.type(descriptor)
|
||||
|
||||
add(JavaField.newField(fieldName, fieldType, field.access_flags.flags).apply {
|
||||
field.attributes.get(Attribute.ConstantValue)?.also { constAttr ->
|
||||
val constantValue = constAttr as ConstantValue_attribute
|
||||
val constant = classFile.constant_pool.get(constantValue.constantvalue_index)
|
||||
val isStringType = fieldType.isStringType()
|
||||
val javaConstant = if (fieldType.isPrimitive() || isStringType) {
|
||||
toJavaConstant(fieldType.kind, constant, if (isStringType) 's' else null)
|
||||
} else {
|
||||
throw IllegalArgumentException("Invalid field type for constant value: $fieldType")
|
||||
}
|
||||
|
||||
this.constantValue = javaConstant
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun JavaAnnotation.setAnnotationElements(classFile: ClassFile) {
|
||||
methods = RealmList<AnnotationElement>().apply {
|
||||
classFile.methods.forEach { method ->
|
||||
val descriptor = classFile.constant_pool.getUTF8Value(method.descriptor.index)
|
||||
val methodName = method.getName(classFile.constant_pool)
|
||||
val methodType = DescriptorUtils.returnType(descriptor)
|
||||
|
||||
add(
|
||||
newAnnotationElement(
|
||||
methodName, methodType, method.access_flags.flags
|
||||
).apply {
|
||||
val annotationDefault =
|
||||
method.attributes.get(Attribute.AnnotationDefault) as? AnnotationDefault_attribute?
|
||||
if (annotationDefault != null) {
|
||||
val defaultValue = annotationDefault.default_value
|
||||
val value = toAnnotationElementValue(defaultValue, classFile)
|
||||
this.defaultValue = value.let(RealmAny::valueOf) ?: RealmAny.nullValue()
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun toAnnotationElementValue(
|
||||
defaultValue: element_value, classFile: ClassFile
|
||||
): IAnnotationElementValue {
|
||||
val char = defaultValue.tag.toChar()
|
||||
var kind = JavaType.kindForType(char)
|
||||
if (char == 's') {
|
||||
kind = JavaType.KIND_REF
|
||||
}
|
||||
|
||||
var value: IAnnotationElementValue? = when (kind) {
|
||||
JavaType.KIND_BOOLEAN,
|
||||
JavaType.KIND_BYTE,
|
||||
JavaType.KIND_CHAR,
|
||||
JavaType.KIND_SHORT,
|
||||
JavaType.KIND_INT,
|
||||
JavaType.KIND_LONG,
|
||||
JavaType.KIND_FLOAT,
|
||||
JavaType.KIND_DOUBLE -> PrimitiveAnnotationElementValue.newInstance(
|
||||
// annotation value kinds are same as JavaType.KIND_*
|
||||
kind = kind,
|
||||
value = toPrimitiveConstant(
|
||||
classFile.constant_pool, kind, char, defaultValue as Primitive_element_value
|
||||
)
|
||||
)
|
||||
|
||||
else -> null
|
||||
}
|
||||
|
||||
if (value == null) {
|
||||
value = when (char) {
|
||||
's' -> PrimitiveAnnotationElementValue.newInstance(
|
||||
kind = IAnnotationElementValue.KIND_STRING,
|
||||
value = toPrimitiveConstant(
|
||||
classFile.constant_pool,
|
||||
JavaType.KIND_REF,
|
||||
char,
|
||||
defaultValue as Primitive_element_value
|
||||
)
|
||||
)
|
||||
|
||||
'e' -> {
|
||||
val enum = defaultValue as Enum_element_value
|
||||
EnumAnnotationElementValue.newInstance(
|
||||
name = classFile.constant_pool.getUTF8Value(enum.const_name_index),
|
||||
type = DescriptorUtils.type(classFile.constant_pool.getUTF8Value(enum.type_name_index))
|
||||
)
|
||||
}
|
||||
|
||||
'c' -> {
|
||||
// even though the name is given as "class_info_index", the constant at that index
|
||||
// is actually a CONSTANT_Utf8_info representing a return type descriptor (JLS $4.3.3)
|
||||
// this has been clearly mentioned in the Java Language Specification
|
||||
ClassAnnotationElementValue.newInstance(
|
||||
DescriptorUtils.type(
|
||||
classFile.constant_pool.getUTF8Value(
|
||||
(defaultValue as Class_element_value).class_info_index
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
'@' -> {
|
||||
val annotation = (defaultValue as Annotation_element_value).annotation_value
|
||||
AnnotationAnnotationElementValue.newInstance(type = DescriptorUtils.type(
|
||||
classFile.constant_pool.getUTF8Value(
|
||||
annotation.type_index
|
||||
)
|
||||
),
|
||||
values = RealmDictionary<RealmAny>().apply {
|
||||
for (pair in annotation.element_value_pairs) {
|
||||
this[classFile.constant_pool.getUTF8Value(pair.element_name_index)] =
|
||||
toAnnotationElementValue(pair.value, classFile).let { elementValue ->
|
||||
RealmAny.valueOf(elementValue)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
'[' -> {
|
||||
ArrayAnnotationElementValue.newInstance(values = RealmList<RealmAny>().apply {
|
||||
(defaultValue as Array_element_value).values.forEach { element ->
|
||||
add(toAnnotationElementValue(element, classFile).let { elementValue ->
|
||||
RealmAny.valueOf(elementValue)
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
else -> throw IllegalArgumentException("Unknown value tag: ${defaultValue.tag}")
|
||||
}
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
private fun toPrimitiveConstant(
|
||||
constantPool: ConstantPool, kind: Int, char: Char, prim: Primitive_element_value
|
||||
): JavaConstant {
|
||||
if (kind == JavaType.KIND_REF && char != 's') {
|
||||
throw IllegalArgumentException("Unknown value tag: $char")
|
||||
}
|
||||
|
||||
val constant = constantPool.get(prim.const_value_index)
|
||||
return toJavaConstant(kind, constant, char)
|
||||
}
|
||||
|
||||
private fun toJavaConstant(
|
||||
kind: Int, constant: CPInfo, char: Char?
|
||||
): JavaConstant {
|
||||
val realmAny = when (kind) {
|
||||
JavaType.KIND_BOOLEAN,
|
||||
JavaType.KIND_BYTE,
|
||||
JavaType.KIND_CHAR,
|
||||
JavaType.KIND_SHORT,
|
||||
JavaType.KIND_INT -> (constant as CONSTANT_Integer_info).value.let {
|
||||
RealmAny.valueOf(it)
|
||||
}
|
||||
|
||||
JavaType.KIND_LONG -> (constant as CONSTANT_Long_info).value.let { RealmAny.valueOf(it) }
|
||||
JavaType.KIND_FLOAT -> (constant as CONSTANT_Float_info).value.let { RealmAny.valueOf(it) }
|
||||
JavaType.KIND_DOUBLE -> (constant as CONSTANT_Double_info).value.let { RealmAny.valueOf(it) }
|
||||
else -> if (char == 's') {
|
||||
if (constant is CONSTANT_String_info) {
|
||||
constant.string
|
||||
} else {
|
||||
(constant as CONSTANT_Utf8_info).value
|
||||
}.let { RealmAny.valueOf(it) }
|
||||
} else {
|
||||
throw IllegalArgumentException("Unknown value tag: $char")
|
||||
}
|
||||
}
|
||||
|
||||
return JavaConstant.newInstance(
|
||||
kind = kind,
|
||||
value = realmAny
|
||||
)
|
||||
}
|
||||
}
|
||||
-91
@@ -1,91 +0,0 @@
|
||||
/*
|
||||
* This file is part of AndroidIDE.
|
||||
*
|
||||
* AndroidIDE is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* AndroidIDE is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.itsaky.androidide.lsp.java.indexing.classfile
|
||||
|
||||
import com.itsaky.androidide.lsp.java.indexing.IJavaSymbol
|
||||
import io.realm.RealmAny
|
||||
import io.realm.RealmResults
|
||||
import io.realm.annotations.Index
|
||||
import io.realm.annotations.LinkingObjects
|
||||
import io.realm.annotations.RealmClass
|
||||
import io.realm.annotations.RealmField
|
||||
import io.realm.annotations.Required
|
||||
|
||||
/**
|
||||
* A method defined in an annotation type.
|
||||
*
|
||||
* @property defaultValue The default value of the annotation element.
|
||||
* @author Akash Yadav
|
||||
*/
|
||||
@RealmClass(embedded = true)
|
||||
open class AnnotationElement : IJavaSymbol {
|
||||
|
||||
@Index
|
||||
@Required
|
||||
@RealmField("name")
|
||||
var name: String? = null
|
||||
|
||||
@RealmField("type")
|
||||
var type: JavaType? = null
|
||||
|
||||
@RealmField("accessFlags")
|
||||
override var accessFlags: Int = 0
|
||||
|
||||
@LinkingObjects("methods")
|
||||
val ofClass: RealmResults<JavaAnnotation>? = null
|
||||
|
||||
/**
|
||||
* The default value of the annotation element. One of the following:
|
||||
* - [PrimitiveAnnotationElementValue]
|
||||
* - [EnumAnnotationElementValue]
|
||||
* - [ArrayAnnotationElementValue]
|
||||
* - [ClassAnnotationElementValue]
|
||||
* - [AnnotationAnnotationElementValue]
|
||||
*/
|
||||
@RealmField("defaultValue")
|
||||
var defaultValue: RealmAny? = null
|
||||
|
||||
/**
|
||||
* Returns the signature of the method.
|
||||
*/
|
||||
fun signature(): String {
|
||||
val sb = StringBuilder()
|
||||
sb.append(name)
|
||||
sb.append("(")
|
||||
sb.append(")")
|
||||
sb.append(type?.internalForm())
|
||||
return sb.toString()
|
||||
}
|
||||
|
||||
companion object {
|
||||
@JvmStatic
|
||||
fun newAnnotationElement(
|
||||
name: String,
|
||||
type: JavaType,
|
||||
accessFlags: Int,
|
||||
defaultValue: RealmAny? = null
|
||||
): AnnotationElement {
|
||||
return AnnotationElement().apply {
|
||||
this.name = name
|
||||
this.type = type
|
||||
this.accessFlags = accessFlags
|
||||
this.defaultValue = defaultValue
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
-105
@@ -1,105 +0,0 @@
|
||||
/*
|
||||
* This file is part of AndroidIDE.
|
||||
*
|
||||
* AndroidIDE is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* AndroidIDE is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.itsaky.androidide.lsp.java.indexing.classfile
|
||||
|
||||
import com.itsaky.androidide.lsp.java.indexing.IJavaType
|
||||
import io.realm.RealmList
|
||||
import io.realm.annotations.Index
|
||||
import io.realm.annotations.PrimaryKey
|
||||
import io.realm.annotations.RealmClass
|
||||
import io.realm.annotations.RealmField
|
||||
import io.realm.annotations.Required
|
||||
|
||||
/**
|
||||
* A Java annotation type.
|
||||
*
|
||||
* Implementation note:
|
||||
* An annotation type is a Java interface, and hence, it inherits from [JavaInterface] instead of
|
||||
* [IJavaType]. Also, [IJavaType.isInterface] and [IJavaType.isAnnotation] both
|
||||
* return `true` for annotation types.
|
||||
*
|
||||
* @author Akash Yadav
|
||||
*/
|
||||
|
||||
@RealmClass
|
||||
open class JavaAnnotation : IJavaType<JavaField, AnnotationElement> {
|
||||
|
||||
@Index
|
||||
@PrimaryKey
|
||||
@Required
|
||||
@RealmField("fqn")
|
||||
override var fqn: String? = null
|
||||
|
||||
@Required
|
||||
@RealmField("name")
|
||||
override var name: String? = null
|
||||
|
||||
@Required
|
||||
@RealmField("pck")
|
||||
override var packageName: String? = null
|
||||
|
||||
@RealmField("isInner")
|
||||
override var isInner: Boolean = false
|
||||
|
||||
@RealmField("superClassFqn")
|
||||
override var superClassFqn: String? = null
|
||||
|
||||
@RealmField("superInterfacesFqn")
|
||||
override var superInterfacesFqn: RealmList<String>? = null
|
||||
|
||||
@RealmField("accessFlags")
|
||||
override var accessFlags: Int = 0
|
||||
|
||||
@RealmField("fields")
|
||||
override var fields: RealmList<JavaField>? = null
|
||||
|
||||
@RealmField("methods")
|
||||
override var methods: RealmList<AnnotationElement>? = null
|
||||
|
||||
override val isInterface: Boolean
|
||||
get() = true
|
||||
|
||||
override val isAnnotation: Boolean
|
||||
get() = true
|
||||
|
||||
companion object {
|
||||
@JvmStatic
|
||||
fun newInstance(
|
||||
fqn: String,
|
||||
name: String,
|
||||
packageName: String,
|
||||
accessFlags: Int,
|
||||
isInner: Boolean = false,
|
||||
superInterfacesFqn: RealmList<String>? = null,
|
||||
fields: RealmList<JavaField>? = null,
|
||||
methods: RealmList<AnnotationElement>? = null
|
||||
): JavaAnnotation {
|
||||
return JavaAnnotation().apply {
|
||||
this.fqn = fqn
|
||||
this.name = name
|
||||
this.packageName = packageName
|
||||
this.isInner = isInner
|
||||
this.superClassFqn = superClassFqn
|
||||
this.superInterfacesFqn = superInterfacesFqn
|
||||
this.accessFlags = accessFlags
|
||||
this.fields = fields
|
||||
this.methods = methods
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,98 +0,0 @@
|
||||
/*
|
||||
* This file is part of AndroidIDE.
|
||||
*
|
||||
* AndroidIDE is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* AndroidIDE is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.itsaky.androidide.lsp.java.indexing.classfile
|
||||
|
||||
import com.itsaky.androidide.lsp.java.indexing.IJavaType
|
||||
import io.realm.RealmList
|
||||
import io.realm.annotations.Index
|
||||
import io.realm.annotations.PrimaryKey
|
||||
import io.realm.annotations.RealmClass
|
||||
import io.realm.annotations.RealmField
|
||||
import io.realm.annotations.Required
|
||||
|
||||
/**
|
||||
* A Java class type.
|
||||
*
|
||||
* @author Akash Yadav
|
||||
*/
|
||||
|
||||
@RealmClass
|
||||
open class JavaClass : IJavaType<JavaField, JavaMethod> {
|
||||
|
||||
@Index
|
||||
@PrimaryKey
|
||||
@Required
|
||||
@RealmField("fqn")
|
||||
override var fqn: String? = null
|
||||
|
||||
@Required
|
||||
@RealmField("name")
|
||||
override var name: String? = null
|
||||
|
||||
@Required
|
||||
@RealmField("pck")
|
||||
override var packageName: String? = null
|
||||
|
||||
@RealmField("isInner")
|
||||
override var isInner: Boolean = false
|
||||
|
||||
@RealmField("superClassFqn")
|
||||
override var superClassFqn: String? = null
|
||||
|
||||
@RealmField("superInterfacesFqn")
|
||||
override var superInterfacesFqn: RealmList<String>? = null
|
||||
|
||||
@RealmField("accessFlags")
|
||||
override var accessFlags: Int = 0
|
||||
|
||||
@RealmField("fields")
|
||||
override var fields: RealmList<JavaField>? = null
|
||||
|
||||
@RealmField("methods")
|
||||
override var methods: RealmList<JavaMethod>? = null
|
||||
|
||||
override val isClass: Boolean
|
||||
get() = true
|
||||
|
||||
companion object {
|
||||
@JvmStatic
|
||||
fun newInstance(
|
||||
fqn: String,
|
||||
name: String,
|
||||
packageName: String,
|
||||
accessFlags: Int,
|
||||
isInner: Boolean = false,
|
||||
superClassFqn: String? = null,
|
||||
superInterfacesFqn: RealmList<String>? = null,
|
||||
fields: RealmList<JavaField>? = null,
|
||||
methods: RealmList<JavaMethod>? = null
|
||||
): JavaClass {
|
||||
return JavaClass().apply {
|
||||
this.fqn = fqn
|
||||
this.name = name
|
||||
this.packageName = packageName
|
||||
this.isInner = isInner
|
||||
this.superClassFqn = superClassFqn
|
||||
this.superInterfacesFqn = superInterfacesFqn
|
||||
this.accessFlags = accessFlags
|
||||
this.fields = fields
|
||||
this.methods = methods
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
-152
@@ -1,152 +0,0 @@
|
||||
/*
|
||||
* This file is part of AndroidIDE.
|
||||
*
|
||||
* AndroidIDE is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* AndroidIDE is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.itsaky.androidide.lsp.java.indexing.classfile
|
||||
|
||||
import com.google.common.base.Objects
|
||||
import com.itsaky.androidide.lsp.java.indexing.ISharedJavaIndexable
|
||||
import io.realm.RealmAny
|
||||
import io.realm.annotations.PrimaryKey
|
||||
import io.realm.annotations.RealmClass
|
||||
import io.realm.annotations.RealmField
|
||||
|
||||
/**
|
||||
* A constant value in Java.
|
||||
*
|
||||
* A unique ID is used as the primary key in order to ensure that a constant is not stored multiple
|
||||
* times in the database. Also, a string constant always has type [JavaType.KIND_REF]. A constant
|
||||
* is never of type [JavaType.KIND_VOID].
|
||||
*
|
||||
* @property id A unique ID which is generated for the constant.
|
||||
* @property kind The kind of contant. See [JavaType] for a list of kinds.
|
||||
* @property value The value of the constant. Cast this value based on the value of [kind].
|
||||
* @author Akash Yadav
|
||||
*/
|
||||
@RealmClass
|
||||
open class JavaConstant : ISharedJavaIndexable {
|
||||
|
||||
@PrimaryKey
|
||||
@RealmField("id")
|
||||
override var id: Int? = null
|
||||
|
||||
@RealmField("kind")
|
||||
var kind: Int = JavaType.KIND_UNKNOWN
|
||||
private set
|
||||
|
||||
@RealmField("value")
|
||||
var value: RealmAny? = null
|
||||
private set
|
||||
|
||||
companion object {
|
||||
fun newInstance(kind: Int, value: RealmAny): JavaConstant {
|
||||
return JavaConstant().apply {
|
||||
this.kind = kind
|
||||
this.value = value
|
||||
this.computeId()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun update(kind: Int = this.kind, value: RealmAny? = this.value) {
|
||||
this.kind = kind
|
||||
this.value = value
|
||||
this.computeId()
|
||||
}
|
||||
|
||||
fun update(src: JavaConstant) {
|
||||
this.kind = src.kind
|
||||
this.value = src.value
|
||||
this.computeId()
|
||||
}
|
||||
|
||||
|
||||
override fun computeId() {
|
||||
this.id = Objects.hashCode(this.kind, this.kind, this.value)
|
||||
}
|
||||
|
||||
// boolean, byte, short, int are stored as int in the bytecode
|
||||
|
||||
fun asBoolean(): Boolean? {
|
||||
return safeCast(JavaType.KIND_BOOLEAN) { asInteger() == 1 }
|
||||
}
|
||||
|
||||
fun asByte(): Byte? {
|
||||
return safeCast(JavaType.KIND_BYTE) { asInteger()?.toByte() }
|
||||
}
|
||||
|
||||
fun asShort(): Short? {
|
||||
return safeCast(JavaType.KIND_SHORT) { asInteger()?.toShort() }
|
||||
}
|
||||
|
||||
fun asInt(): Int? {
|
||||
return safeCast(JavaType.KIND_INT) { asInteger() }
|
||||
}
|
||||
|
||||
fun asLong(): Long? {
|
||||
return safeCast(JavaType.KIND_LONG) { asLong() }
|
||||
}
|
||||
|
||||
fun asFloat(): Float? {
|
||||
return safeCast(JavaType.KIND_FLOAT) { asFloat() }
|
||||
}
|
||||
|
||||
fun asDouble(): Double? {
|
||||
return safeCast(JavaType.KIND_DOUBLE) { asDouble() }
|
||||
}
|
||||
|
||||
fun asString(): String? {
|
||||
return safeCast(JavaType.KIND_REF) { asString() }
|
||||
}
|
||||
|
||||
fun asChar(): Char? {
|
||||
return safeCast(JavaType.KIND_CHAR) { asInteger()?.toChar() }
|
||||
}
|
||||
|
||||
private inline fun <R> safeCast(kind: Int, action: RealmAny.() -> R): R? {
|
||||
return value?.let {
|
||||
if (this.kind == kind) {
|
||||
try {
|
||||
it.action()
|
||||
} catch (ex: ClassCastException) {
|
||||
throw IllegalStateException("Kind == $kind but safeCast failed", ex)
|
||||
}
|
||||
} else null
|
||||
}
|
||||
}
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (other !is JavaConstant) return false
|
||||
|
||||
if (id != other.id) return false
|
||||
if (kind != other.kind) return false
|
||||
if (value != other.value) return false
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
var result = id ?: 0
|
||||
result = 31 * result + kind
|
||||
result = 31 * result + (value?.hashCode() ?: 0)
|
||||
return result
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
return "JavaConstant(id=$id, kind=$kind, value=$value)"
|
||||
}
|
||||
}
|
||||
@@ -1,97 +0,0 @@
|
||||
/*
|
||||
* This file is part of AndroidIDE.
|
||||
*
|
||||
* AndroidIDE is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* AndroidIDE is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.itsaky.androidide.lsp.java.indexing.classfile
|
||||
|
||||
import com.itsaky.androidide.lsp.java.indexing.IJavaType
|
||||
import io.realm.RealmList
|
||||
import io.realm.annotations.Index
|
||||
import io.realm.annotations.PrimaryKey
|
||||
import io.realm.annotations.RealmClass
|
||||
import io.realm.annotations.RealmField
|
||||
import io.realm.annotations.Required
|
||||
|
||||
/**
|
||||
* A Java enum type.
|
||||
*
|
||||
* @author Akash Yadav
|
||||
*/
|
||||
@RealmClass
|
||||
open class JavaEnum : IJavaType<JavaField, JavaMethod> {
|
||||
|
||||
@Index
|
||||
@PrimaryKey
|
||||
@Required
|
||||
@RealmField("fqn")
|
||||
override var fqn: String? = null
|
||||
|
||||
@Required
|
||||
@RealmField("name")
|
||||
override var name: String? = null
|
||||
|
||||
@Required
|
||||
@RealmField("pck")
|
||||
override var packageName: String? = null
|
||||
|
||||
@RealmField("isInner")
|
||||
override var isInner: Boolean = false
|
||||
|
||||
@RealmField("superClassFqn")
|
||||
override var superClassFqn: String? = null
|
||||
|
||||
@RealmField("superInterfacesFqn")
|
||||
override var superInterfacesFqn: RealmList<String>? = null
|
||||
|
||||
@RealmField("accessFlags")
|
||||
override var accessFlags: Int = 0
|
||||
|
||||
@RealmField("fields")
|
||||
override var fields: RealmList<JavaField>? = null
|
||||
|
||||
@RealmField("methods")
|
||||
override var methods: RealmList<JavaMethod>? = null
|
||||
|
||||
override val isEnum: Boolean
|
||||
get() = true
|
||||
|
||||
companion object {
|
||||
@JvmStatic
|
||||
fun newInstance(
|
||||
fqn: String,
|
||||
name: String,
|
||||
packageName: String,
|
||||
accessFlags: Int,
|
||||
isInner: Boolean = false,
|
||||
superClassFqn: String? = null,
|
||||
superInterfacesFqn: RealmList<String>? = null,
|
||||
fields: RealmList<JavaField>? = null,
|
||||
methods: RealmList<JavaMethod>? = null
|
||||
): JavaEnum {
|
||||
return JavaEnum().apply {
|
||||
this.fqn = fqn
|
||||
this.name = name
|
||||
this.packageName = packageName
|
||||
this.isInner = isInner
|
||||
this.superClassFqn = superClassFqn
|
||||
this.superInterfacesFqn = superInterfacesFqn
|
||||
this.accessFlags = accessFlags
|
||||
this.fields = fields
|
||||
this.methods = methods
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,65 +0,0 @@
|
||||
/*
|
||||
* This file is part of AndroidIDE.
|
||||
*
|
||||
* AndroidIDE is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* AndroidIDE is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.itsaky.androidide.lsp.java.indexing.classfile
|
||||
|
||||
import com.itsaky.androidide.lsp.java.indexing.IJavaSymbol
|
||||
import io.realm.RealmResults
|
||||
import io.realm.annotations.Index
|
||||
import io.realm.annotations.LinkingObjects
|
||||
import io.realm.annotations.RealmClass
|
||||
import io.realm.annotations.RealmField
|
||||
|
||||
/**
|
||||
* @author Akash Yadav
|
||||
*/
|
||||
@RealmClass(embedded = true)
|
||||
open class JavaField : IJavaSymbol {
|
||||
|
||||
@Index
|
||||
@RealmField("name")
|
||||
var name: String? = null
|
||||
|
||||
@RealmField("type")
|
||||
var type: JavaType? = null
|
||||
|
||||
@RealmField("accessFlags")
|
||||
override var accessFlags: Int = 0
|
||||
|
||||
@RealmField("constantValue")
|
||||
var constantValue: JavaConstant? = null
|
||||
|
||||
@LinkingObjects("fields")
|
||||
val ofClass: RealmResults<JavaClass>? = null
|
||||
|
||||
companion object {
|
||||
@JvmStatic
|
||||
fun newField(
|
||||
name: String,
|
||||
type: JavaType,
|
||||
accessFlags: Int,
|
||||
constantValue: JavaConstant? = null
|
||||
): JavaField {
|
||||
return JavaField().apply {
|
||||
this.name = name
|
||||
this.type = type
|
||||
this.accessFlags = accessFlags
|
||||
this.constantValue = constantValue
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
-96
@@ -1,96 +0,0 @@
|
||||
/*
|
||||
* This file is part of AndroidIDE.
|
||||
*
|
||||
* AndroidIDE is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* AndroidIDE is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.itsaky.androidide.lsp.java.indexing.classfile
|
||||
|
||||
import com.itsaky.androidide.lsp.java.indexing.IJavaType
|
||||
import io.realm.RealmList
|
||||
import io.realm.annotations.Index
|
||||
import io.realm.annotations.PrimaryKey
|
||||
import io.realm.annotations.RealmClass
|
||||
import io.realm.annotations.RealmField
|
||||
import io.realm.annotations.Required
|
||||
|
||||
/**
|
||||
* A Java interface type.
|
||||
*
|
||||
* @author Akash Yadav
|
||||
*/
|
||||
|
||||
@RealmClass
|
||||
open class JavaInterface : IJavaType<JavaField, JavaMethod> {
|
||||
|
||||
@Index
|
||||
@PrimaryKey
|
||||
@Required
|
||||
@RealmField("fqn")
|
||||
override var fqn: String? = null
|
||||
|
||||
@Required
|
||||
@RealmField("name")
|
||||
override var name: String? = null
|
||||
|
||||
@Required
|
||||
@RealmField("pck")
|
||||
override var packageName: String? = null
|
||||
|
||||
@RealmField("isInner")
|
||||
override var isInner: Boolean = false
|
||||
|
||||
@RealmField("superClassFqn")
|
||||
override var superClassFqn: String? = null
|
||||
|
||||
@RealmField("superInterfacesFqn")
|
||||
override var superInterfacesFqn: RealmList<String>? = null
|
||||
|
||||
@RealmField("accessFlags")
|
||||
override var accessFlags: Int = 0
|
||||
|
||||
@RealmField("fields")
|
||||
override var fields: RealmList<JavaField>? = null
|
||||
|
||||
@RealmField("methods")
|
||||
override var methods: RealmList<JavaMethod>? = null
|
||||
|
||||
override val isInterface: Boolean
|
||||
get() = true
|
||||
|
||||
companion object {
|
||||
@JvmStatic
|
||||
fun newInstance(
|
||||
fqn: String,
|
||||
name: String,
|
||||
packageName: String,
|
||||
accessFlags: Int,
|
||||
isInner: Boolean = false,
|
||||
superInterfacesFqn: RealmList<String>? = null,
|
||||
fields: RealmList<JavaField>? = null,
|
||||
methods: RealmList<JavaMethod>? = null
|
||||
): JavaInterface {
|
||||
return JavaInterface().apply {
|
||||
this.fqn = fqn
|
||||
this.name = name
|
||||
this.packageName = packageName
|
||||
this.isInner = isInner
|
||||
this.superInterfacesFqn = superInterfacesFqn
|
||||
this.accessFlags = accessFlags
|
||||
this.fields = fields
|
||||
this.methods = methods
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
-83
@@ -1,83 +0,0 @@
|
||||
/*
|
||||
* This file is part of AndroidIDE.
|
||||
*
|
||||
* AndroidIDE is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* AndroidIDE is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.itsaky.androidide.lsp.java.indexing.classfile
|
||||
|
||||
import com.itsaky.androidide.lsp.java.indexing.IJavaSymbol
|
||||
import io.realm.RealmList
|
||||
import io.realm.RealmResults
|
||||
import io.realm.annotations.Index
|
||||
import io.realm.annotations.LinkingObjects
|
||||
import io.realm.annotations.RealmClass
|
||||
import io.realm.annotations.RealmField
|
||||
import io.realm.annotations.Required
|
||||
|
||||
/**
|
||||
* @author Akash Yadav
|
||||
*/
|
||||
|
||||
@RealmClass(embedded = true)
|
||||
open class JavaMethod : IJavaSymbol {
|
||||
@Index
|
||||
@Required
|
||||
@RealmField("name")
|
||||
var name: String? = null
|
||||
|
||||
@RealmField("paramTypes")
|
||||
var paramsTypes: RealmList<JavaType>? = null
|
||||
|
||||
@RealmField("returnType")
|
||||
var returnType: JavaType? = null
|
||||
|
||||
@RealmField("accessFlags")
|
||||
override var accessFlags: Int = 0
|
||||
|
||||
@LinkingObjects("methods")
|
||||
val ofClass: RealmResults<JavaClass>? = null
|
||||
|
||||
/**
|
||||
* Returns the signature of the method.
|
||||
*/
|
||||
fun signature(): String {
|
||||
val sb = StringBuilder()
|
||||
sb.append(name)
|
||||
sb.append("(")
|
||||
paramsTypes?.forEach {
|
||||
sb.append(it.internalForm())
|
||||
}
|
||||
sb.append(")")
|
||||
sb.append(returnType?.internalForm())
|
||||
return sb.toString()
|
||||
}
|
||||
|
||||
companion object {
|
||||
@JvmStatic
|
||||
fun newInstance(
|
||||
name: String,
|
||||
paramsTypes: RealmList<JavaType>,
|
||||
returnType: JavaType,
|
||||
accessFlags: Int,
|
||||
): JavaMethod {
|
||||
return JavaMethod().apply {
|
||||
this.name = name
|
||||
this.paramsTypes = paramsTypes
|
||||
this.returnType = returnType
|
||||
this.accessFlags = accessFlags
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
-340
@@ -1,340 +0,0 @@
|
||||
/*
|
||||
* This file is part of AndroidIDE.
|
||||
*
|
||||
* AndroidIDE is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* AndroidIDE is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.itsaky.androidide.lsp.java.indexing.classfile
|
||||
|
||||
import com.google.common.base.Objects
|
||||
import com.itsaky.androidide.db.utils.hash
|
||||
import com.itsaky.androidide.db.utils.isEqualTo
|
||||
import com.itsaky.androidide.lsp.java.indexing.ISharedJavaIndexable
|
||||
import io.realm.RealmAny
|
||||
import io.realm.RealmDictionary
|
||||
import io.realm.RealmList
|
||||
import io.realm.annotations.PrimaryKey
|
||||
import io.realm.annotations.RealmClass
|
||||
import io.realm.annotations.RealmField
|
||||
import io.realm.annotations.Required
|
||||
|
||||
sealed interface IAnnotationElementValue : ISharedJavaIndexable {
|
||||
var kind: Int
|
||||
|
||||
companion object {
|
||||
const val KIND_UNKNOWN = JavaType.KIND_UNKNOWN
|
||||
const val KIND_BOOLEAN = JavaType.KIND_BOOLEAN
|
||||
const val KIND_BYTE = JavaType.KIND_BYTE
|
||||
const val KIND_CHAR = JavaType.KIND_CHAR
|
||||
const val KIND_DOUBLE = JavaType.KIND_DOUBLE
|
||||
const val KIND_FLOAT = JavaType.KIND_FLOAT
|
||||
const val KIND_INT = JavaType.KIND_INT
|
||||
const val KIND_LONG = JavaType.KIND_LONG
|
||||
const val KIND_SHORT = JavaType.KIND_SHORT
|
||||
|
||||
const val KIND_STRING = JavaType.__KIND_STRING
|
||||
const val KIND_CLASS = JavaType.__KIND_CLASS
|
||||
const val KIND_ENUM = JavaType.__KIND_ENUM
|
||||
const val KIND_ANNOTATION = JavaType.__KIND_ANNOTATION
|
||||
const val KIND_ARRAY = JavaType.__KIND_ARRAY
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@RealmClass
|
||||
open class PrimitiveAnnotationElementValue : IAnnotationElementValue {
|
||||
|
||||
@PrimaryKey
|
||||
@Required
|
||||
@RealmField("id")
|
||||
override var id: Int? = null
|
||||
|
||||
@RealmField("kind")
|
||||
override var kind: Int = 0
|
||||
|
||||
@RealmField("value")
|
||||
var value: JavaConstant? = null
|
||||
|
||||
override fun computeId() {
|
||||
this.id = Objects.hashCode(this.kind, this.value)
|
||||
}
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (other !is PrimitiveAnnotationElementValue) return false
|
||||
|
||||
if (id != other.id) return false
|
||||
if (kind != other.kind) return false
|
||||
if (value != other.value) return false
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
var result = id ?: 0
|
||||
result = 31 * result + kind
|
||||
result = 31 * result + (value?.hashCode() ?: 0)
|
||||
return result
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
return "PrimitiveAnnotationElementValue(id=$id, kind=$kind, value=$value)"
|
||||
}
|
||||
|
||||
companion object {
|
||||
@JvmStatic
|
||||
fun newInstance(kind: Int, value: JavaConstant): PrimitiveAnnotationElementValue {
|
||||
return PrimitiveAnnotationElementValue().apply {
|
||||
this.kind = kind
|
||||
this.value = value
|
||||
this.computeId()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@RealmClass
|
||||
open class EnumAnnotationElementValue : IAnnotationElementValue {
|
||||
@PrimaryKey
|
||||
@Required
|
||||
@RealmField("id")
|
||||
override var id: Int? = null
|
||||
|
||||
@RealmField("kind")
|
||||
override var kind: Int = IAnnotationElementValue.KIND_ENUM
|
||||
|
||||
@RealmField("name")
|
||||
var name: String? = null
|
||||
|
||||
@RealmField("type")
|
||||
var type: JavaType? = null
|
||||
|
||||
override fun computeId() {
|
||||
this.id = Objects.hashCode(this.kind, this.name, this.type)
|
||||
}
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (other !is EnumAnnotationElementValue) return false
|
||||
|
||||
if (id != other.id) return false
|
||||
if (kind != other.kind) return false
|
||||
if (name != other.name) return false
|
||||
if (type != other.type) return false
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
var result = id ?: 0
|
||||
result = 31 * result + kind
|
||||
result = 31 * result + (name?.hashCode() ?: 0)
|
||||
result = 31 * result + (type?.hashCode() ?: 0)
|
||||
return result
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
return "EnumAnnotationElementValue(id=$id, kind=$kind, name=$name, type=$type)"
|
||||
}
|
||||
|
||||
companion object {
|
||||
@JvmStatic
|
||||
fun newInstance(name: String, type: JavaType): EnumAnnotationElementValue {
|
||||
return EnumAnnotationElementValue().apply {
|
||||
this.name = name
|
||||
this.type = type
|
||||
this.computeId()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@RealmClass
|
||||
open class ArrayAnnotationElementValue : IAnnotationElementValue {
|
||||
|
||||
@PrimaryKey
|
||||
@Required
|
||||
@RealmField("id")
|
||||
override var id: Int? = null
|
||||
|
||||
@RealmField("kind")
|
||||
override var kind: Int = IAnnotationElementValue.KIND_ARRAY
|
||||
|
||||
@RealmField("numValues")
|
||||
var numValues: Int = 0
|
||||
|
||||
/**
|
||||
* The elements of the array element value. Always of type [IAnnotationElementValue].
|
||||
*/
|
||||
@RealmField("values")
|
||||
var values: RealmList<RealmAny>? = null
|
||||
|
||||
override fun computeId() {
|
||||
this.id = Objects.hashCode(this.kind, this.numValues, this.values)
|
||||
}
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (other !is ArrayAnnotationElementValue) return false
|
||||
|
||||
if (id != other.id) return false
|
||||
if (kind != other.kind) return false
|
||||
if (numValues != other.numValues) return false
|
||||
if (values != other.values) return false
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
var result = id ?: 0
|
||||
result = 31 * result + kind
|
||||
result = 31 * result + numValues
|
||||
result = 31 * result + (values?.hashCode() ?: 0)
|
||||
return result
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
return "ArrayAnnotationElementValue(id=$id, kind=$kind, numValues=$numValues, values=$values)"
|
||||
}
|
||||
|
||||
companion object {
|
||||
@JvmStatic
|
||||
fun newInstance(values: RealmList<RealmAny>): ArrayAnnotationElementValue {
|
||||
return ArrayAnnotationElementValue().apply {
|
||||
this.numValues = values.size
|
||||
this.values = values
|
||||
this.computeId()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@RealmClass
|
||||
open class ClassAnnotationElementValue : IAnnotationElementValue {
|
||||
|
||||
@PrimaryKey
|
||||
@Required
|
||||
@RealmField("id")
|
||||
override var id: Int? = null
|
||||
|
||||
@RealmField("kind")
|
||||
override var kind: Int = IAnnotationElementValue.KIND_CLASS
|
||||
|
||||
@RealmField("type")
|
||||
var type: JavaType? = null
|
||||
|
||||
override fun computeId() {
|
||||
this.id = Objects.hashCode(this.kind, this.type)
|
||||
}
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (other !is ClassAnnotationElementValue) return false
|
||||
|
||||
if (id != other.id) return false
|
||||
if (kind != other.kind) return false
|
||||
if (type != other.type) return false
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
var result = id ?: 0
|
||||
result = 31 * result + kind
|
||||
result = 31 * result + (type?.hashCode() ?: 0)
|
||||
return result
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
return "ClassAnnotationElementValue(id=$id, kind=$kind, type=$type)"
|
||||
}
|
||||
|
||||
companion object {
|
||||
@JvmStatic
|
||||
fun newInstance(type: JavaType): ClassAnnotationElementValue {
|
||||
return ClassAnnotationElementValue().apply {
|
||||
this.type = type
|
||||
this.computeId()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@RealmClass
|
||||
open class AnnotationAnnotationElementValue : IAnnotationElementValue {
|
||||
|
||||
@PrimaryKey
|
||||
@Required
|
||||
@RealmField("id")
|
||||
override var id: Int? = null
|
||||
|
||||
@RealmField("kind")
|
||||
override var kind: Int = IAnnotationElementValue.KIND_ANNOTATION
|
||||
|
||||
@RealmField("type")
|
||||
var type: JavaType? = null
|
||||
|
||||
/**
|
||||
* The named elements of the annotation element value. Always of type [IAnnotationElementValue].
|
||||
*/
|
||||
@RealmField("values")
|
||||
var values: RealmDictionary<RealmAny>? = null
|
||||
|
||||
override fun computeId() {
|
||||
// RealmDictionary seems to not override hashCode()
|
||||
this.id = Objects.hashCode(this.kind, this.type, this.values?.hash())
|
||||
}
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (other !is AnnotationAnnotationElementValue) return false
|
||||
|
||||
if (id != other.id) return false
|
||||
if (kind != other.kind) return false
|
||||
if (type != other.type) return false
|
||||
if (!values.isEqualTo(other)) return false
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
var result = id ?: 0
|
||||
result = 31 * result + kind
|
||||
result = 31 * result + (type?.hashCode() ?: 0)
|
||||
result = 31 * result + (values?.hashCode() ?: 0)
|
||||
return result
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
return "AnnotationAnnotationElementValue(id=$id, kind=$kind, type=$type, values=$values)"
|
||||
}
|
||||
|
||||
companion object {
|
||||
@JvmStatic
|
||||
fun newInstance(
|
||||
type: JavaType,
|
||||
values: RealmDictionary<RealmAny>
|
||||
): AnnotationAnnotationElementValue {
|
||||
return AnnotationAnnotationElementValue().apply {
|
||||
this.type = type
|
||||
this.values = values
|
||||
this.computeId()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
-43
@@ -1,43 +0,0 @@
|
||||
/*
|
||||
* This file is part of AndroidIDE.
|
||||
*
|
||||
* AndroidIDE is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* AndroidIDE is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.itsaky.androidide.lsp.java.indexing.internal
|
||||
|
||||
import com.google.auto.service.AutoService
|
||||
import com.itsaky.androidide.indexing.IIndexService
|
||||
import com.itsaky.androidide.projects.IWorkspace
|
||||
import java.io.File
|
||||
|
||||
/**
|
||||
* @author Akash Yadav
|
||||
*/
|
||||
@AutoService(IIndexService::class)
|
||||
internal class JavaIndexServiceImpl : IIndexService {
|
||||
|
||||
override val displayName: String
|
||||
get() = "Java LSP Indexing Service"
|
||||
|
||||
override fun scanFiles(workspace: IWorkspace): Collection<File> {
|
||||
return emptySet()
|
||||
}
|
||||
|
||||
override suspend fun indexFiles(
|
||||
workspace: IWorkspace,
|
||||
files: Collection<File>
|
||||
) {
|
||||
}
|
||||
}
|
||||
@@ -1,232 +0,0 @@
|
||||
/*
|
||||
* This file is part of AndroidIDE.
|
||||
*
|
||||
* AndroidIDE is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* AndroidIDE is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.itsaky.androidide.lsp.java.utils
|
||||
|
||||
import com.itsaky.androidide.lsp.java.indexing.IJavaType
|
||||
import com.itsaky.androidide.lsp.java.indexing.classfile.AnnotationElement
|
||||
import com.itsaky.androidide.lsp.java.indexing.classfile.JavaField
|
||||
import com.itsaky.androidide.lsp.java.indexing.classfile.JavaMethod
|
||||
import com.itsaky.androidide.lsp.java.indexing.classfile.JavaType
|
||||
import java.util.Objects
|
||||
|
||||
/**
|
||||
* @author Akash Yadav
|
||||
*/
|
||||
object DescriptorUtils {
|
||||
|
||||
/**
|
||||
* Create a method ID for the given method.
|
||||
*/
|
||||
fun createMethodId(
|
||||
klass: IJavaType<*, *>,
|
||||
method: JavaMethod
|
||||
): Int {
|
||||
return Objects.hash(klass.fqn, method.name, method.paramsTypes, method.returnType)
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a method ID for the given method.
|
||||
*/
|
||||
fun createMethodId(
|
||||
klass: IJavaType<*, *>,
|
||||
element: AnnotationElement
|
||||
): Int {
|
||||
return Objects.hash(klass.fqn, element.name, element.type)
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a field ID for the given field. The string representation is similar to how fields
|
||||
* are represented in Smali.
|
||||
*
|
||||
* ```
|
||||
* Lcom/example/Foo;->bar:I
|
||||
* ```
|
||||
*/
|
||||
fun createFieldId(
|
||||
klass: IJavaType<*, *>,
|
||||
field: JavaField
|
||||
): Int {
|
||||
return Objects.hash(klass.fqn, field.name, field.type)
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the return type of a method descriptor.
|
||||
*
|
||||
* @param descriptor The method descriptor.
|
||||
*/
|
||||
fun returnType(descriptor: String): JavaType {
|
||||
val idx = descriptor.indexOfLast { it == ')' }
|
||||
if (descriptor[0] != '(' || idx == -1) {
|
||||
throw IllegalArgumentException("Invalid method descriptor: $descriptor")
|
||||
}
|
||||
|
||||
return type(descriptor.substring(idx + 1))
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the parameter types of a method descriptor.
|
||||
*
|
||||
* @param descriptor The method descriptor.
|
||||
*/
|
||||
fun paramTypes(descriptor: String): List<JavaType> {
|
||||
val idx = descriptor.indexOfLast { it == ')' }
|
||||
if (descriptor[0] != '(' || idx == -1) {
|
||||
throw IllegalArgumentException("Invalid method descriptor: $descriptor")
|
||||
}
|
||||
|
||||
val types = mutableListOf<JavaType>()
|
||||
val params = descriptor.substring(1, idx)
|
||||
var i = 0
|
||||
while (i < params.length) {
|
||||
types.add(
|
||||
when (params[i]) {
|
||||
'L' -> {
|
||||
val end = params.indexOf(';', startIndex = i + 1)
|
||||
referenceType(params, fromIndex = i, toIndex = end).also {
|
||||
i = end + 1
|
||||
}
|
||||
}
|
||||
|
||||
'[' -> arrayType(params, fromIndex = i).also {
|
||||
// number of '['
|
||||
// + length of type (I or java/lang/String)
|
||||
// + 2 if kind is ref (because refs have 'L' and ';')
|
||||
i += it.arrayDims + it.name!!.length + (if (it.kind == JavaType.KIND_REF) 2 else 0)
|
||||
}
|
||||
|
||||
else -> primitive(params[i].toString()).also {
|
||||
++i
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
return types
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a type descriptor. A type descriptor can be any of :
|
||||
* - a primitive type descriptor
|
||||
* - a reference type descriptor
|
||||
* - an array type descriptor
|
||||
*
|
||||
* @param descriptor The type descriptor.
|
||||
*/
|
||||
fun type(descriptor: String): JavaType {
|
||||
if (descriptor.startsWith("L")) {
|
||||
return referenceType(descriptor)
|
||||
}
|
||||
|
||||
if (descriptor.startsWith("[")) {
|
||||
return arrayType(descriptor)
|
||||
}
|
||||
|
||||
if (descriptor.length != 1) {
|
||||
throw IllegalArgumentException("Invalid type descriptor: $descriptor")
|
||||
}
|
||||
|
||||
return primitive(descriptor)
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a primitive type descriptor.
|
||||
*
|
||||
* @param descriptor The primitive type descriptor.
|
||||
*/
|
||||
fun primitive(descriptor: String): JavaType = primitive(descriptor[0])
|
||||
|
||||
/**
|
||||
* Parse a primitive type descriptor.
|
||||
*
|
||||
* @param char The primitive type.
|
||||
*/
|
||||
fun primitive(char: Char): JavaType {
|
||||
val primitive = JavaType.primitiveFor(char)
|
||||
return requireNotNull(primitive) {
|
||||
"Invalid type descriptor: $char"
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a reference type descriptor.
|
||||
*
|
||||
* Format:
|
||||
* ```
|
||||
* Lcom/example/Foo;
|
||||
* ```
|
||||
*
|
||||
* @param descriptor The descriptor of a reference type
|
||||
* @param fromIndex The index of the first character of the reference type name.
|
||||
* @param toIndex The index of the last character of the reference type name (inclusive).
|
||||
*/
|
||||
fun referenceType(
|
||||
descriptor: String, fromIndex: Int = 0, toIndex: Int = descriptor.length - 1
|
||||
): JavaType {
|
||||
if (descriptor[fromIndex] != 'L' || descriptor[toIndex] != ';') {
|
||||
throw IllegalArgumentException("Invalid reference type descriptor: $descriptor, fromIndex: $fromIndex, toIndex: $toIndex")
|
||||
}
|
||||
|
||||
return when (descriptor) {
|
||||
"Ljava/lang/String;" -> JavaType.STRING
|
||||
"Ljava/lang/Object;" -> JavaType.OBJECT
|
||||
"Ljava/lang/Class;" -> JavaType.CLASS
|
||||
else -> {
|
||||
// remove leading 'L' and trailing ';'
|
||||
val name = descriptor.substring(fromIndex + 1, toIndex)
|
||||
JavaType.newInstance(name = name, kind = JavaType.KIND_REF)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Parse an array type descriptor.
|
||||
*
|
||||
* Format:
|
||||
* ```
|
||||
* [I
|
||||
* [[I
|
||||
* [[Lcom/example/Foo;
|
||||
* ```
|
||||
*
|
||||
* @param descriptor The array type descriptor.
|
||||
* @param fromIndex The index of the first character of the array type descriptor.
|
||||
*/
|
||||
fun arrayType(descriptor: String, fromIndex: Int = 0): JavaType {
|
||||
if (descriptor[fromIndex] != '[') {
|
||||
throw IllegalArgumentException("Invalid array type descriptor: $descriptor, fromIndex: $fromIndex")
|
||||
}
|
||||
|
||||
var idx = fromIndex + 1
|
||||
var dims = 1
|
||||
while (descriptor[idx] == '[') {
|
||||
idx++
|
||||
dims++
|
||||
}
|
||||
|
||||
val type = if (descriptor[idx] == 'L') {
|
||||
referenceType(
|
||||
descriptor, fromIndex = idx, toIndex = descriptor.indexOf(';', startIndex = idx + 1)
|
||||
)
|
||||
} else {
|
||||
primitive(descriptor[idx])
|
||||
}
|
||||
|
||||
return JavaType.newInstance(name = type.name, kind = type.kind, arrayDims = dims)
|
||||
}
|
||||
}
|
||||
+4
-49
@@ -15,38 +15,17 @@
|
||||
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.itsaky.androidide.lsp.java.indexing.classfile
|
||||
|
||||
import com.google.common.base.Objects
|
||||
import com.itsaky.androidide.lsp.java.indexing.ISharedJavaIndexable
|
||||
import com.itsaky.androidide.models.ICloneable
|
||||
import io.realm.annotations.Index
|
||||
import io.realm.annotations.PrimaryKey
|
||||
import io.realm.annotations.RealmClass
|
||||
import io.realm.annotations.RealmField
|
||||
import io.realm.annotations.Required
|
||||
package com.itsaky.androidide.lsp.java.utils
|
||||
|
||||
/**
|
||||
* @author Akash Yadav
|
||||
*/
|
||||
open class JavaType {
|
||||
|
||||
@RealmClass
|
||||
open class JavaType : ISharedJavaIndexable, ICloneable {
|
||||
|
||||
@Required
|
||||
@PrimaryKey
|
||||
@RealmField("id")
|
||||
override var id: Int? = null
|
||||
|
||||
@Index
|
||||
@Required
|
||||
@RealmField("name")
|
||||
var name: String? = null
|
||||
|
||||
@RealmField("kind")
|
||||
var kind: Int = KIND_UNKNOWN
|
||||
|
||||
@RealmField("arrayDims")
|
||||
var arrayDims: Int = 0
|
||||
|
||||
/**
|
||||
@@ -76,14 +55,7 @@ open class JavaType : ISharedJavaIndexable, ICloneable {
|
||||
*/
|
||||
fun isPrimitive(): Boolean {
|
||||
return when (kind) {
|
||||
KIND_BOOLEAN,
|
||||
KIND_BYTE,
|
||||
KIND_CHAR,
|
||||
KIND_DOUBLE,
|
||||
KIND_FLOAT,
|
||||
KIND_INT,
|
||||
KIND_LONG,
|
||||
KIND_SHORT -> true
|
||||
KIND_BOOLEAN, KIND_BYTE, KIND_CHAR, KIND_DOUBLE, KIND_FLOAT, KIND_INT, KIND_LONG, KIND_SHORT -> true
|
||||
|
||||
else -> false
|
||||
}
|
||||
@@ -117,14 +89,6 @@ open class JavaType : ISharedJavaIndexable, ICloneable {
|
||||
return name == CLASS.name
|
||||
}
|
||||
|
||||
override fun clone(): JavaType {
|
||||
return newInstance(name = name, kind = kind, arrayDims = arrayDims)
|
||||
}
|
||||
|
||||
override fun computeId() {
|
||||
this.id = Objects.hashCode(this.kind, this.name, this.arrayDims)
|
||||
}
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (other !is JavaType) return false
|
||||
@@ -144,7 +108,7 @@ open class JavaType : ISharedJavaIndexable, ICloneable {
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
return "JavaType(id=$id, name=$name, kind=$kind, arrayDims=$arrayDims)"
|
||||
return "JavaType(name=$name, kind=$kind, arrayDims=$arrayDims)"
|
||||
}
|
||||
|
||||
companion object {
|
||||
@@ -160,14 +124,6 @@ open class JavaType : ISharedJavaIndexable, ICloneable {
|
||||
const val KIND_VOID = 8
|
||||
const val KIND_REF = 9
|
||||
|
||||
// kind values used in IAnnotationElementValue
|
||||
// defined here for convenience
|
||||
internal const val __KIND_STRING = 10
|
||||
internal const val __KIND_CLASS = 11
|
||||
internal const val __KIND_ENUM = 12
|
||||
internal const val __KIND_ANNOTATION = 13
|
||||
internal const val __KIND_ARRAY = 14
|
||||
|
||||
const val TYPE_BOOLEAN = 'Z'
|
||||
const val TYPE_BYTE = 'B'
|
||||
const val TYPE_CHAR = 'C'
|
||||
@@ -264,7 +220,6 @@ open class JavaType : ISharedJavaIndexable, ICloneable {
|
||||
this.name = name
|
||||
this.kind = kind
|
||||
this.arrayDims = arrayDims
|
||||
this.computeId()
|
||||
}
|
||||
}
|
||||
|
||||
-65
@@ -1,65 +0,0 @@
|
||||
/*
|
||||
* This file is part of AndroidIDE.
|
||||
*
|
||||
* AndroidIDE is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* AndroidIDE is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.itsaky.androidide.lsp.java.indexing
|
||||
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import com.itsaky.androidide.utils.FileProvider
|
||||
import org.junit.rules.TestRule
|
||||
import org.junit.runner.Description
|
||||
import org.junit.runners.model.Statement
|
||||
import java.io.File
|
||||
|
||||
/**
|
||||
* @author Akash Yadav
|
||||
*/
|
||||
class AndroidJarModelingRule : TestRule {
|
||||
|
||||
private val androidJar: File by lazy {
|
||||
FileProvider.projectRoot()
|
||||
.resolve("utilities")
|
||||
.resolve("framework-stubs")
|
||||
.resolve("libs")
|
||||
.resolve("android.jar")
|
||||
.toFile().also {
|
||||
check(it.exists()) {
|
||||
"android.jar file could not be found"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private val androidJarIndexer: JavaJarModelBuilder by lazy {
|
||||
assertThat(androidJar.exists()).isTrue()
|
||||
JavaJarModelBuilder(androidJar)
|
||||
}
|
||||
|
||||
val types: List<IJavaType<*, *>> by lazy {
|
||||
androidJarIndexer.buildTypes()
|
||||
}
|
||||
|
||||
override fun apply(base: Statement?, description: Description?): Statement {
|
||||
|
||||
// call 'types' to build the type models
|
||||
types
|
||||
|
||||
base?.evaluate()
|
||||
|
||||
return object : Statement() {
|
||||
override fun evaluate() {}
|
||||
}
|
||||
}
|
||||
}
|
||||
-121
@@ -1,121 +0,0 @@
|
||||
/*
|
||||
* This file is part of AndroidIDE.
|
||||
*
|
||||
* AndroidIDE is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* AndroidIDE is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.itsaky.androidide.lsp.java.indexing
|
||||
|
||||
import com.itsaky.androidide.lsp.java.indexing.classfile.JavaType
|
||||
import com.itsaky.androidide.lsp.java.utils.DescriptorUtils
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.robolectric.RobolectricTestRunner
|
||||
|
||||
/**
|
||||
* @author Akash Yadav
|
||||
*/
|
||||
@RunWith(RobolectricTestRunner::class)
|
||||
class DescriptorUtilsTest {
|
||||
|
||||
@Test
|
||||
fun testReturnType() {
|
||||
val descriptor1 = "(I)V"
|
||||
val expected1 = JavaType.newInstance(name = "V", kind = JavaType.KIND_VOID)
|
||||
assertEquals(expected1, DescriptorUtils.returnType(descriptor1))
|
||||
|
||||
val descriptor2 = "(Ljava/lang/String;)I"
|
||||
val expected2 = JavaType.newInstance(name = "I", kind = JavaType.KIND_INT)
|
||||
assertEquals(expected2, DescriptorUtils.returnType(descriptor2))
|
||||
|
||||
val descriptor3 = "(I[Ljava/lang/String;)V"
|
||||
val expected3 = JavaType.newInstance(name = "V", kind = JavaType.KIND_VOID)
|
||||
assertEquals(expected3, DescriptorUtils.returnType(descriptor3))
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException::class)
|
||||
fun testReturnTypeInvalidDescriptor() {
|
||||
DescriptorUtils.returnType("Ljava/lang/String;I")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testParamTypes() {
|
||||
val descriptor1 = "(I)V"
|
||||
val expected1 = listOf(JavaType.newInstance(name = "I", kind = JavaType.KIND_INT))
|
||||
assertEquals(expected1, DescriptorUtils.paramTypes(descriptor1))
|
||||
|
||||
val descriptor2 = "(Ljava/lang/String;I)V"
|
||||
val expected2 = listOf(
|
||||
JavaType.newInstance(name = "java/lang/String", kind = JavaType.KIND_REF),
|
||||
JavaType.newInstance(name = "I", kind = JavaType.KIND_INT)
|
||||
)
|
||||
assertEquals(expected2, DescriptorUtils.paramTypes(descriptor2))
|
||||
|
||||
val descriptor3 = "([I[Ljava/lang/String;)V"
|
||||
val expected3 = listOf(
|
||||
JavaType.newInstance(name = "I", kind = JavaType.KIND_INT, arrayDims = 1),
|
||||
JavaType.newInstance(name = "java/lang/String", kind = JavaType.KIND_REF, arrayDims = 1)
|
||||
)
|
||||
assertEquals(expected3, DescriptorUtils.paramTypes(descriptor3))
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException::class)
|
||||
fun testParamTypesInvalidDescriptor() {
|
||||
DescriptorUtils.paramTypes("Ljava/lang/String;I")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testPrimitive() {
|
||||
val booleanType = JavaType.newInstance(name = "Z", kind = JavaType.KIND_BOOLEAN)
|
||||
assertEquals(booleanType, DescriptorUtils.primitive('Z'))
|
||||
|
||||
val intType = JavaType.newInstance(name = "I", kind = JavaType.KIND_INT)
|
||||
assertEquals(intType, DescriptorUtils.primitive('I'))
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException::class)
|
||||
fun testPrimitiveInvalidType() {
|
||||
DescriptorUtils.primitive('X')
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testReferenceType() {
|
||||
val descriptor = "Ljava/lang/String;"
|
||||
val expected = JavaType.newInstance(name = "java/lang/String", kind = JavaType.KIND_REF)
|
||||
assertEquals(expected, DescriptorUtils.referenceType(descriptor))
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException::class)
|
||||
fun testReferenceTypeInvalidDescriptor() {
|
||||
DescriptorUtils.referenceType("Ljava/lang/String")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testArrayType() {
|
||||
val descriptor1 = "[I"
|
||||
val expected1 = JavaType.newInstance(name = "I", kind = JavaType.KIND_INT, arrayDims = 1)
|
||||
assertEquals(expected1, DescriptorUtils.arrayType(descriptor1))
|
||||
|
||||
val descriptor2 = "[[Ljava/lang/String;"
|
||||
val expected2 =
|
||||
JavaType.newInstance(name = "java/lang/String", kind = JavaType.KIND_REF, arrayDims = 2)
|
||||
assertEquals(expected2, DescriptorUtils.arrayType(descriptor2))
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException::class)
|
||||
fun testArrayTypeInvalidDescriptor() {
|
||||
DescriptorUtils.arrayType("Ljava/lang/String;")
|
||||
}
|
||||
}
|
||||
-524
@@ -1,524 +0,0 @@
|
||||
/*
|
||||
* This file is part of AndroidIDE.
|
||||
*
|
||||
* AndroidIDE is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* AndroidIDE is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.itsaky.androidide.lsp.java.indexing
|
||||
|
||||
import android.view.View
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import com.itsaky.androidide.lsp.java.indexing.classfile.AnnotationElement
|
||||
import com.itsaky.androidide.lsp.java.indexing.classfile.IAnnotationElementValue
|
||||
import com.itsaky.androidide.lsp.java.indexing.classfile.JavaAnnotation
|
||||
import com.itsaky.androidide.lsp.java.indexing.classfile.JavaClass
|
||||
import com.itsaky.androidide.lsp.java.indexing.classfile.JavaEnum
|
||||
import com.itsaky.androidide.lsp.java.indexing.classfile.JavaField
|
||||
import com.itsaky.androidide.lsp.java.indexing.classfile.JavaInterface
|
||||
import com.itsaky.androidide.lsp.java.indexing.classfile.JavaMethod
|
||||
import com.itsaky.androidide.lsp.java.indexing.classfile.JavaType
|
||||
import com.itsaky.androidide.lsp.java.indexing.classfile.PrimitiveAnnotationElementValue
|
||||
import io.realm.RealmAny
|
||||
import openjdk.tools.classfile.AccessFlags
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.robolectric.RobolectricTestRunner
|
||||
import java.lang.reflect.Modifier
|
||||
|
||||
/**
|
||||
* @author Akash Yadav
|
||||
*/
|
||||
@RunWith(RobolectricTestRunner::class)
|
||||
class JavaJarModelBuilderTest {
|
||||
|
||||
@Rule
|
||||
@JvmField
|
||||
val indexRule = AndroidJarModelingRule()
|
||||
|
||||
private val types: List<IJavaType<*, *>>
|
||||
get() = indexRule.types
|
||||
|
||||
@Test
|
||||
fun `test JavaClass properties`() {
|
||||
val sampleClass = types.firstOrNull { it.fqn == "java/lang/String" }
|
||||
|
||||
assertThat(sampleClass).isNotNull()
|
||||
sampleClass?.let {
|
||||
assertThat(it.fqn).isEqualTo("java/lang/String")
|
||||
assertThat(it.name).isEqualTo("String")
|
||||
assertThat(it.packageName).isEqualTo("java/lang")
|
||||
assertThat(it.superClassFqn).isEqualTo("java/lang/Object")
|
||||
assertThat(it.superInterfacesFqn).containsAtLeast(
|
||||
"java/io/Serializable",
|
||||
"java/lang/Comparable",
|
||||
"java/lang/CharSequence"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test JavaField properties`() {
|
||||
val sampleClass = types.firstOrNull { it.fqn == "java/lang/String" }
|
||||
val fields = sampleClass?.fields
|
||||
|
||||
assertThat(fields).isNotEmpty()
|
||||
fields?.forEach { f ->
|
||||
val field = f as JavaField
|
||||
assertThat(field.name).isNotEmpty()
|
||||
assertThat(field.accessFlags).isGreaterThan(0)
|
||||
assertThat(field.type).isNotNull()
|
||||
}
|
||||
|
||||
// java.lang.String in Android only has a single public field
|
||||
assertThat(fields?.size).isEqualTo(1)
|
||||
|
||||
val field = fields?.firstOrNull() as JavaField?
|
||||
assertThat(field).isNotNull()
|
||||
assertThat(field?.name).isEqualTo("CASE_INSENSITIVE_ORDER")
|
||||
assertThat(field?.type?.name).isEqualTo("java/util/Comparator")
|
||||
assertThat(field?.type?.internalForm()).isEqualTo("Ljava/util/Comparator;")
|
||||
assertThat(field?.type?.kind).isEqualTo(JavaType.KIND_REF)
|
||||
assertThat(Modifier.isPublic(field?.accessFlags ?: 0)).isTrue()
|
||||
assertThat(Modifier.isStatic(field?.accessFlags ?: 0)).isTrue()
|
||||
assertThat(Modifier.isFinal(field?.accessFlags ?: 0)).isTrue()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test JavaMethod properties`() {
|
||||
val sampleClass = types.firstOrNull { it.fqn == "java/lang/String" }
|
||||
val methods = sampleClass?.methods
|
||||
|
||||
assertThat(methods).isNotEmpty()
|
||||
methods?.forEach { mth ->
|
||||
val method = mth as JavaMethod
|
||||
assertThat(method.name).isNotEmpty()
|
||||
assertThat(method.accessFlags).isGreaterThan(0)
|
||||
assertThat(method.paramsTypes).isNotNull()
|
||||
assertThat(method.returnType).isNotNull()
|
||||
}
|
||||
|
||||
assertThat(methods?.size).isAtLeast(1)
|
||||
|
||||
val method = (methods as List<JavaMethod>?)?.firstOrNull { it.name == "compareTo" }
|
||||
assertThat(method).isNotNull()
|
||||
assertThat(method?.name).isEqualTo("compareTo")
|
||||
assertThat(method?.paramsTypes?.size).isEqualTo(1)
|
||||
assertThat(method?.returnType?.name).isEqualTo("I")
|
||||
assertThat(Modifier.isPublic(method?.accessFlags ?: 0)).isTrue()
|
||||
assertThat(Modifier.isStatic(method?.accessFlags ?: 0)).isFalse()
|
||||
assertThat(Modifier.isFinal(method?.accessFlags ?: 0)).isFalse()
|
||||
|
||||
val param = method?.paramsTypes?.firstOrNull()
|
||||
assertThat(param).isNotNull()
|
||||
assertThat(param?.name).isEqualTo("java/lang/String")
|
||||
assertThat(param?.internalForm()).isEqualTo("Ljava/lang/String;")
|
||||
assertThat(param?.kind).isEqualTo(JavaType.KIND_REF)
|
||||
assertThat(param?.arrayDims).isEqualTo(0)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test JavaType properties`() {
|
||||
val sampleClass = types.firstOrNull { it.fqn == "java/lang/String" }
|
||||
val fields = sampleClass?.fields
|
||||
val methods = sampleClass?.methods
|
||||
|
||||
fields?.forEach { f ->
|
||||
val field = f as JavaField
|
||||
assertThat(field.type?.name).isNotEmpty()
|
||||
assertThat(field.type?.kind).isNotEqualTo(JavaType.KIND_UNKNOWN)
|
||||
}
|
||||
|
||||
methods?.forEach { mth ->
|
||||
val method = mth as JavaMethod
|
||||
method.paramsTypes?.forEach { paramType ->
|
||||
assertThat(paramType.name).isNotEmpty()
|
||||
assertThat(paramType.kind).isNotEqualTo(JavaType.KIND_UNKNOWN)
|
||||
}
|
||||
assertThat(method.returnType?.name).isNotEmpty()
|
||||
assertThat(method.returnType?.kind).isNotEqualTo(JavaType.KIND_UNKNOWN)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test integer constant field values`() {
|
||||
val viewClass: IJavaType<JavaField, JavaMethod> =
|
||||
types.first { it.fqn == "android/view/View" } as IJavaType<JavaField, JavaMethod>
|
||||
assertThat(viewClass.fields).isNotEmpty()
|
||||
|
||||
val fields = viewClass.fields!!
|
||||
val visible = fields.firstOrNull { it.name == "VISIBLE" }
|
||||
assertThat(visible).isNotNull()
|
||||
assertThat(visible?.type).isEqualTo(JavaType.INT)
|
||||
|
||||
val constantValue = visible?.constantValue
|
||||
assertThat(constantValue).isNotNull()
|
||||
assertThat(constantValue?.kind).isEqualTo(JavaType.KIND_INT)
|
||||
assertThat(constantValue?.value?.type).isEqualTo(RealmAny.Type.INTEGER)
|
||||
assertThat(constantValue?.asInt()).isNotNull()
|
||||
assertThat(constantValue?.asInt()).isEqualTo(View.VISIBLE)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test object type`() {
|
||||
val obj = types.firstOrNull { it.fqn == "java/lang/Object" }
|
||||
assertThat(obj).isNotNull()
|
||||
assertThat(obj?.name).isEqualTo("Object")
|
||||
assertThat(obj?.superClassFqn).isNull()
|
||||
assertThat(obj?.superInterfacesFqn).isEmpty()
|
||||
assertThat(obj?.fields).isEmpty()
|
||||
assertThat(obj?.methods).isNotEmpty()
|
||||
assertThat(obj?.isAnnotation).isFalse()
|
||||
assertThat(obj?.isInterface).isFalse()
|
||||
assertThat(obj?.isEnum).isFalse()
|
||||
assertThat(obj?.isClass).isTrue()
|
||||
assertThat(obj).isInstanceOf(JavaClass::class.java)
|
||||
assertThat(obj?.packageName).isEqualTo("java/lang")
|
||||
|
||||
val access = AccessFlags(obj!!.accessFlags)
|
||||
assertThat(access.`is`(AccessFlags.ACC_PUBLIC)).isTrue()
|
||||
assertThat(access.`is`(AccessFlags.ACC_STATIC)).isFalse()
|
||||
assertThat(access.`is`(AccessFlags.ACC_INTERFACE)).isFalse()
|
||||
assertThat(access.`is`(AccessFlags.ACC_ANNOTATION)).isFalse()
|
||||
assertThat(access.`is`(AccessFlags.ACC_ENUM)).isFalse()
|
||||
assertThat(access.`is`(AccessFlags.ACC_ABSTRACT)).isFalse()
|
||||
assertThat(access.`is`(AccessFlags.ACC_FINAL)).isFalse()
|
||||
assertThat(access.`is`(AccessFlags.ACC_NATIVE)).isFalse()
|
||||
assertThat(access.`is`(AccessFlags.ACC_NATIVE)).isFalse()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test class simple name`() {
|
||||
val obj = types.firstOrNull { it.fqn == "android/view/View" }
|
||||
assertThat(obj).isNotNull()
|
||||
assertThat(obj?.name).isEqualTo("View")
|
||||
assertThat(obj?.packageName).isEqualTo("android/view")
|
||||
assertThat(obj?.superClassFqn).isEqualTo("java/lang/Object")
|
||||
assertThat(obj?.superInterfacesFqn).isNotEmpty()
|
||||
assertThat(obj?.fields).isNotEmpty()
|
||||
assertThat(obj?.methods).isNotEmpty()
|
||||
assertThat(obj?.isAnnotation).isFalse()
|
||||
assertThat(obj?.isInterface).isFalse()
|
||||
assertThat(obj?.isEnum).isFalse()
|
||||
assertThat(obj?.isClass).isTrue()
|
||||
assertThat(obj).isInstanceOf(JavaClass::class.java)
|
||||
|
||||
val access = AccessFlags(obj!!.accessFlags)
|
||||
assertThat(access.`is`(AccessFlags.ACC_PUBLIC)).isTrue()
|
||||
assertThat(access.`is`(AccessFlags.ACC_STATIC)).isFalse()
|
||||
assertThat(access.`is`(AccessFlags.ACC_INTERFACE)).isFalse()
|
||||
assertThat(access.`is`(AccessFlags.ACC_ANNOTATION)).isFalse()
|
||||
assertThat(access.`is`(AccessFlags.ACC_ENUM)).isFalse()
|
||||
assertThat(access.`is`(AccessFlags.ACC_ABSTRACT)).isFalse()
|
||||
assertThat(access.`is`(AccessFlags.ACC_FINAL)).isFalse()
|
||||
assertThat(access.`is`(AccessFlags.ACC_NATIVE)).isFalse()
|
||||
assertThat(access.`is`(AccessFlags.ACC_NATIVE)).isFalse()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test class superclass name`() {
|
||||
types.firstOrNull { it.fqn == "android/view/View" }.also {
|
||||
assertThat(it).isNotNull()
|
||||
assertThat(it?.superClassFqn).isEqualTo("java/lang/Object")
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test class super interface names`() {
|
||||
types.firstOrNull { it.fqn == "android/view/View" }.also {
|
||||
assertThat(it).isNotNull()
|
||||
assertThat(it?.superInterfacesFqn).containsExactly(
|
||||
"android/graphics/drawable/Drawable\$Callback",
|
||||
"android/view/KeyEvent\$Callback",
|
||||
"android/view/accessibility/AccessibilityEventSource"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test interface super interface names`() {
|
||||
types.firstOrNull { it.fqn == "java/io/Closeable" }.also {
|
||||
assertThat(it).isNotNull()
|
||||
assertThat(it?.superInterfacesFqn).containsExactly("java/lang/AutoCloseable")
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test class fields`() {
|
||||
types.firstOrNull { it.fqn == "android/view/View" }.also { view ->
|
||||
assertThat(view).isNotNull()
|
||||
assertThat(view?.fields?.map { field -> (field as JavaField).name }).containsAtLeast(
|
||||
"VISIBLE",
|
||||
"INVISIBLE",
|
||||
"GONE",
|
||||
"FOCUS_DOWN",
|
||||
"FOCUS_UP",
|
||||
"FOCUS_LEFT",
|
||||
"FOCUS_RIGHT",
|
||||
"FOCUS_BACKWARD",
|
||||
"FOCUS_FORWARD",
|
||||
)
|
||||
}
|
||||
|
||||
types.firstOrNull { it.fqn == "android/view/ViewGroup\$LayoutParams" }.also { view ->
|
||||
assertThat(view).isNotNull()
|
||||
assertThat(view?.fields?.map { field -> (field as JavaField).name }).containsAtLeast(
|
||||
"WRAP_CONTENT",
|
||||
"MATCH_PARENT",
|
||||
"FILL_PARENT",
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test interface fields`() {
|
||||
types.firstOrNull { it.fqn == "android/provider/CalendarContract\$CalendarSyncColumns" }
|
||||
.also { view ->
|
||||
assertThat(view).isNotNull()
|
||||
assertThat(view?.fields?.map { field -> (field as JavaField).name }).containsAtLeast(
|
||||
"CAL_SYNC1",
|
||||
"CAL_SYNC2",
|
||||
"CAL_SYNC3",
|
||||
"CAL_SYNC4",
|
||||
"CAL_SYNC5",
|
||||
"CAL_SYNC6",
|
||||
"CAL_SYNC7",
|
||||
"CAL_SYNC8",
|
||||
"CAL_SYNC9",
|
||||
"CAL_SYNC10",
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test mandatory enum fields`() {
|
||||
types.filterIsInstance<JavaEnum>().forEach { enum ->
|
||||
assertThat(enum.fields?.map { it.name }).contains("\$VALUES")
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test enum fields`() {
|
||||
types.firstOrNull { it.fqn == "android/net/wifi/SupplicantState" }.also { enum ->
|
||||
assertThat(enum?.fields?.map { (it as JavaField).name })
|
||||
.containsAtLeast(
|
||||
"DISCONNECTED",
|
||||
"INTERFACE_DISABLED",
|
||||
"INACTIVE",
|
||||
"SCANNING",
|
||||
"AUTHENTICATING",
|
||||
"ASSOCIATING",
|
||||
"ASSOCIATED",
|
||||
"FOUR_WAY_HANDSHAKE",
|
||||
"COMPLETED",
|
||||
"GROUP_HANDSHAKE",
|
||||
"DORMANT",
|
||||
"UNINITIALIZED",
|
||||
"INVALID"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test annotation fields`() {
|
||||
// android.jar doesn't seem to contain annotation classes with some fields
|
||||
// TODO: add test for verifying how annotation fields are handled
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test class methods`() {
|
||||
types.firstOrNull { it.fqn == "android/view/View" }.also { view ->
|
||||
assertThat(view).isNotNull()
|
||||
assertThat(view?.methods?.map { field -> (field as JavaMethod).signature() }).containsAtLeast(
|
||||
"setId(I)V",
|
||||
"getId()I",
|
||||
"setOnClickListener(Landroid/view/View\$OnClickListener;)V",
|
||||
"hasOnClickListeners()Z",
|
||||
"hasOnLongClickListeners()Z",
|
||||
"setAccessibilityDelegate(Landroid/view/View\$AccessibilityDelegate;)V",
|
||||
"getAccessibilityDelegate()Landroid/view/View\$AccessibilityDelegate;",
|
||||
"setAccessibilityLiveRegion(I)V",
|
||||
"getAccessibilityLiveRegion()I",
|
||||
"setAccessibilityHeading(Z)V",
|
||||
"isAccessibilityHeading()Z",
|
||||
"setPadding(IIII)V",
|
||||
"setPaddingRelative(IIII)V",
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test interface methods`() {
|
||||
types.firstOrNull { it.fqn == "android/view/View\$OnClickListener" }.also { view ->
|
||||
assertThat(view).isNotNull()
|
||||
assertThat(view?.methods?.map { field -> (field as JavaMethod).signature() })
|
||||
.contains("onClick(Landroid/view/View;)V")
|
||||
}
|
||||
types.firstOrNull { it.fqn == "android/view/View\$OnLongClickListener" }.also { view ->
|
||||
assertThat(view).isNotNull()
|
||||
assertThat(view?.methods?.map { field -> (field as JavaMethod).signature() })
|
||||
.containsAtLeast(
|
||||
"onLongClick(Landroid/view/View;)Z",
|
||||
"onLongClickUseDefaultHapticFeedback(Landroid/view/View;)Z"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test mandatory enum methods`() {
|
||||
types.filterIsInstance<JavaEnum>().forEach { enum ->
|
||||
assertThat(enum.methods?.map { it.signature() }).containsAtLeast(
|
||||
"values()[L${enum.fqn};",
|
||||
"\$values()[L${enum.fqn};",
|
||||
"valueOf(Ljava/lang/String;)L${enum.fqn};",
|
||||
"<clinit>()V"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test enum methods`() {
|
||||
types.firstOrNull { it.fqn == "android/icu/text/DisplayContext" }.also { enum ->
|
||||
assertThat(enum?.methods?.map { (it as JavaMethod).signature() }).contains(
|
||||
"type()Landroid/icu/text/DisplayContext\$Type;"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test annotation methods`() {
|
||||
types.firstOrNull { it.fqn == "android/annotation/TargetApi" }.also { annotation ->
|
||||
assertThat(annotation?.methods?.map { (it as AnnotationElement).signature() }).containsExactly(
|
||||
"value()I"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test annotation methods with default values`() {
|
||||
types.firstOrNull { it.fqn == "java/lang/Deprecated" }.also { annotation ->
|
||||
assertThat(annotation?.methods).isNotEmpty()
|
||||
|
||||
val since =
|
||||
annotation?.methods?.find { (it as AnnotationElement).name == "since" } as AnnotationElement
|
||||
assertThat(since.type).isEqualTo(JavaType.STRING)
|
||||
|
||||
var defValue = since.defaultValue
|
||||
assertThat(defValue).isNotNull()
|
||||
assertThat(defValue?.valueClass).isEqualTo(PrimitiveAnnotationElementValue::class.java)
|
||||
|
||||
var defVal = defValue?.asRealmModel(PrimitiveAnnotationElementValue::class.java)
|
||||
assertThat(defVal).isNotNull()
|
||||
assertThat(defVal?.kind).isEqualTo(IAnnotationElementValue.KIND_STRING)
|
||||
assertThat(defVal?.value).isNotNull()
|
||||
assertThat(defVal?.value?.kind).isEqualTo(JavaType.KIND_REF)
|
||||
assertThat(defVal?.value?.asString()).isEqualTo("")
|
||||
assertThat(defVal?.value?.asBoolean()).isNull()
|
||||
assertThat(defVal?.value?.asByte()).isNull()
|
||||
assertThat(defVal?.value?.asShort()).isNull()
|
||||
assertThat(defVal?.value?.asChar()).isNull()
|
||||
assertThat(defVal?.value?.asInt()).isNull()
|
||||
assertThat(defVal?.value?.asLong()).isNull()
|
||||
assertThat(defVal?.value?.asFloat()).isNull()
|
||||
assertThat(defVal?.value?.asDouble()).isNull()
|
||||
|
||||
val forRemoval =
|
||||
annotation.methods?.find { (it as AnnotationElement).name == "forRemoval" } as AnnotationElement
|
||||
assertThat(forRemoval.type).isEqualTo(JavaType.BOOLEAN)
|
||||
|
||||
defValue = forRemoval.defaultValue
|
||||
assertThat(defValue).isNotNull()
|
||||
assertThat(defValue?.valueClass).isEqualTo(PrimitiveAnnotationElementValue::class.java)
|
||||
|
||||
defVal = defValue?.asRealmModel(PrimitiveAnnotationElementValue::class.java)
|
||||
assertThat(defVal).isNotNull()
|
||||
assertThat(defVal?.kind).isEqualTo(IAnnotationElementValue.KIND_BOOLEAN)
|
||||
assertThat(defVal?.value).isNotNull()
|
||||
assertThat(defVal?.value?.kind).isEqualTo(JavaType.KIND_BOOLEAN)
|
||||
assertThat(defVal?.value?.asBoolean()).isEqualTo(false)
|
||||
assertThat(defVal?.value?.asByte()).isNull()
|
||||
assertThat(defVal?.value?.asShort()).isNull()
|
||||
assertThat(defVal?.value?.asChar()).isNull()
|
||||
assertThat(defVal?.value?.asInt()).isNull()
|
||||
assertThat(defVal?.value?.asLong()).isNull()
|
||||
assertThat(defVal?.value?.asFloat()).isNull()
|
||||
assertThat(defVal?.value?.asDouble()).isNull()
|
||||
assertThat(defVal?.value?.asString()).isNull()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test annotation methods with array value`() {
|
||||
types.firstOrNull { it.fqn == "java/lang/SuppressWarnings" }.also { annotation ->
|
||||
assertThat(annotation?.methods).isNotEmpty()
|
||||
|
||||
val value =
|
||||
annotation?.methods?.find { (it as AnnotationElement).name == "value" } as AnnotationElement
|
||||
assertThat(value.type?.kind).isEqualTo(JavaType.KIND_REF)
|
||||
assertThat(value.type?.name).isEqualTo("java/lang/String")
|
||||
assertThat(value.type?.arrayDims).isEqualTo(1)
|
||||
|
||||
val defValue = value.defaultValue
|
||||
assertThat(defValue).isNull()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test class type classification`() {
|
||||
types.firstOrNull { it.fqn == "android/view/View" }.also {
|
||||
assertThat(it).isNotNull()
|
||||
assertThat(it?.isClass).isTrue()
|
||||
assertThat(it?.isInterface).isFalse()
|
||||
assertThat(it?.isAnnotation).isFalse()
|
||||
assertThat(it?.isEnum).isFalse()
|
||||
assertThat(it).isInstanceOf(JavaClass::class.java)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test interface type classification`() {
|
||||
types.firstOrNull { it.fqn == "android/view/View\$OnClickListener" }.also {
|
||||
assertThat(it).isNotNull()
|
||||
assertThat(it?.isClass).isFalse()
|
||||
assertThat(it?.isInterface).isTrue()
|
||||
assertThat(it?.isAnnotation).isFalse()
|
||||
assertThat(it?.isEnum).isFalse()
|
||||
assertThat(it).isInstanceOf(JavaInterface::class.java)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test annotation type classification`() {
|
||||
types.firstOrNull { it.fqn == "java/lang/Deprecated" }.also {
|
||||
assertThat(it).isNotNull()
|
||||
assertThat(it?.isClass).isFalse()
|
||||
assertThat(it?.isInterface).isTrue()
|
||||
assertThat(it?.isAnnotation).isTrue()
|
||||
assertThat(it?.isEnum).isFalse()
|
||||
assertThat(it).isInstanceOf(JavaAnnotation::class.java)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test enum type classification`() {
|
||||
types.firstOrNull { it.fqn == "android/icu/text/DisplayContext" }.also {
|
||||
assertThat(it).isNotNull()
|
||||
assertThat(it?.isClass).isFalse()
|
||||
assertThat(it?.isInterface).isFalse()
|
||||
assertThat(it?.isAnnotation).isFalse()
|
||||
assertThat(it?.isEnum).isTrue()
|
||||
assertThat(it).isInstanceOf(JavaEnum::class.java)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -126,7 +126,6 @@ include(
|
||||
":core:actions",
|
||||
":core:app",
|
||||
":core:common",
|
||||
":core:database",
|
||||
":core:indexing-api",
|
||||
":core:indexing-core",
|
||||
":core:lsp-api",
|
||||
|
||||
@@ -42,11 +42,6 @@ configurations.api {
|
||||
|
||||
dependencies {
|
||||
|
||||
// compileOnly so that we can provide the APIs for the related test rules
|
||||
// if a module needs to use those APIs, it needs to explicitly add the database module as a
|
||||
// dependency
|
||||
compileOnly(projects.core.database)
|
||||
|
||||
api(libs.google.protobuf)
|
||||
api(libs.tests.androidx.espresso.core)
|
||||
api(libs.tests.androidx.espresso.contrib)
|
||||
|
||||
@@ -66,7 +66,6 @@ dependencies {
|
||||
androidTestImplementation(libs.androidx.benchmark.junit4)
|
||||
|
||||
androidTestImplementation(projects.core.common)
|
||||
androidTestImplementation(projects.core.database)
|
||||
androidTestImplementation(projects.core.indexingApi)
|
||||
androidTestImplementation(projects.java.lsp)
|
||||
androidTestImplementation(projects.utilities.shared)
|
||||
|
||||
Reference in New Issue
Block a user