mirror of
https://gitee.com/zhangsken/AndroidIDE.git
synced 2026-10-16 07:49:44 +08:00
fix(indexing): split indexing logic into 'api' and 'core' modules
This commit is contained in:
Generated
+1
@@ -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.indexing" target="17" />
|
||||
<module name="AndroidIDE.editor-api" target="17" />
|
||||
<module name="AndroidIDE.editor-languages" target="11" />
|
||||
<module name="AndroidIDE.editor-treesitter" target="17" />
|
||||
|
||||
@@ -129,7 +129,8 @@ dependencies {
|
||||
// Local projects here
|
||||
implementation(projects.core.actions)
|
||||
implementation(projects.core.common)
|
||||
implementation(projects.core.indexing)
|
||||
implementation(projects.core.indexingApi)
|
||||
implementation(projects.core.indexingCore)
|
||||
implementation(projects.core.lspApi)
|
||||
implementation(projects.core.projects)
|
||||
implementation(projects.core.resources)
|
||||
|
||||
+1
-1
@@ -15,7 +15,7 @@
|
||||
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.itsaky.androidide.lsp.java.indexing
|
||||
package com.itsaky.androidide.models
|
||||
|
||||
/**
|
||||
* Anything that can be cloned.
|
||||
@@ -54,6 +54,28 @@ interface IRealmProvider {
|
||||
*/
|
||||
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.
|
||||
*/
|
||||
|
||||
@@ -29,7 +29,7 @@ groupConfig {
|
||||
}
|
||||
|
||||
android {
|
||||
namespace = "${BuildConfig.packageName}.indexing"
|
||||
namespace = "${BuildConfig.packageName}.indexing.api"
|
||||
}
|
||||
|
||||
dependencies {
|
||||
@@ -38,6 +38,7 @@ dependencies {
|
||||
|
||||
api(projects.core.common)
|
||||
api(projects.core.database)
|
||||
api(projects.core.projects)
|
||||
api(projects.logging.logger)
|
||||
api(projects.utilities.shared)
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* 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, C : IIndexParams>(
|
||||
protected val params: IIndexParams?
|
||||
) : IIndex<T, C> {
|
||||
|
||||
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()
|
||||
}
|
||||
}
|
||||
+29
-20
@@ -18,16 +18,26 @@
|
||||
package com.itsaky.androidide.indexing
|
||||
|
||||
import com.itsaky.androidide.db.IRealmProvider
|
||||
import com.itsaky.androidide.indexing.internal.DefaultIndex
|
||||
|
||||
/**
|
||||
* An index of symbols of type [IndexableT].
|
||||
*
|
||||
* @author Akash Yadav
|
||||
*/
|
||||
interface IIndex<IndexableT : IIndexable> {
|
||||
interface IIndex<IndexableT : IIndexable, ConfigT : IIndexParams> {
|
||||
|
||||
companion object {
|
||||
|
||||
/**
|
||||
* The default batch size for indexing.
|
||||
*/
|
||||
const val DEFAULT_PUT_BATCH_SIZE = 100
|
||||
|
||||
/**
|
||||
* Base path for indices.
|
||||
*/
|
||||
const val INDEX_BASE_PATH = "${IRealmProvider.PATH_SEPARATOR}index"
|
||||
|
||||
private const val DEF_IS_ASYNC = true
|
||||
|
||||
/**
|
||||
@@ -53,40 +63,39 @@ interface IIndex<IndexableT : IIndexable> {
|
||||
*
|
||||
* @param symbol The symbol to index.
|
||||
*/
|
||||
fun index(symbol: IndexableT, async: Boolean = DEF_IS_ASYNC)
|
||||
fun index(symbol: IndexableT)
|
||||
|
||||
/**
|
||||
* Index the given symbol asynchronously.
|
||||
*
|
||||
* @param symbol The symbol to index.
|
||||
*/
|
||||
fun indexAsync(symbol: IndexableT)
|
||||
|
||||
/**
|
||||
* Index the given symbols.
|
||||
*
|
||||
* @param symbols The symbols to index.
|
||||
*/
|
||||
fun indexAll(symbols: Collection<IndexableT>, async: Boolean = DEF_IS_ASYNC) = indexBatched(
|
||||
symbols,
|
||||
symbols.size
|
||||
)
|
||||
fun indexAll(symbols: Collection<IndexableT>)
|
||||
|
||||
/**
|
||||
* Index the given symbols in batches.
|
||||
* Index the given symbols asynchronously.
|
||||
*
|
||||
* @param symbols The symbols to index.
|
||||
* @param batchSize The number of symbols to index in each batch.
|
||||
*/
|
||||
fun indexBatched(
|
||||
symbols: Collection<IndexableT>,
|
||||
batchSize: Int = DefaultIndex.DEFAULT_PUT_BATCH_SIZE,
|
||||
async: Boolean = DEF_IS_ASYNC,
|
||||
)
|
||||
fun indexAllAsync(symbols: Collection<IndexableT>)
|
||||
|
||||
/**
|
||||
* Create a sub-index in this index.
|
||||
* Create a sub-index in this index. The default implementation will throw an [UnsupportedOperationException].
|
||||
*
|
||||
* @param name The name of the sub-index.
|
||||
* @return The created sub-index.
|
||||
*/
|
||||
fun <I : IIndexable> createSubIndex(
|
||||
name: String,
|
||||
config: IndexConfig? = null
|
||||
): IIndex<I>
|
||||
fun <I : IIndexable, C : IIndexParams> createSubIndex(
|
||||
params: IIndexParams? = null
|
||||
): IIndex<I, C> {
|
||||
throw UnsupportedOperationException()
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete the index.
|
||||
@@ -0,0 +1,161 @@
|
||||
/*
|
||||
* 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 com.itsaky.androidide.utils.ServiceLoader
|
||||
|
||||
/**
|
||||
* A factory for creating instances of [IIndex].
|
||||
*
|
||||
* @param I The indexable type.
|
||||
* @param P The index creation params.
|
||||
* @author Akash Yadav
|
||||
*/
|
||||
interface IIndexFactory<I : IIndexable, P : IIndexParams> {
|
||||
|
||||
/**
|
||||
* The parameters used to create the index.
|
||||
*/
|
||||
var params: P?
|
||||
|
||||
/**
|
||||
* Get the indexable type for the index.
|
||||
*/
|
||||
fun indexableType(): Class<out I>
|
||||
|
||||
/**
|
||||
* Get the parameter type for the index.
|
||||
*/
|
||||
fun paramType(): Class<out P>
|
||||
|
||||
/**
|
||||
* Create the index.
|
||||
*
|
||||
* @return The created [IIndex].
|
||||
*/
|
||||
@Throws(NotFoundException::class)
|
||||
fun create(): IIndex<I, P>
|
||||
|
||||
companion object {
|
||||
|
||||
private val factoryCache =
|
||||
mutableMapOf<Class<out IIndexable>, IIndexFactory<out IIndexable, out IIndexParams>>()
|
||||
|
||||
/**
|
||||
* Finds the index factory for the given symbol type returning `null` if not found.
|
||||
*
|
||||
* @param symTyp The symbol type.
|
||||
* @param paramTyp The parameter type.
|
||||
* @return The index factory.
|
||||
*/
|
||||
fun <T : IIndexable, P : IIndexParams> findFactoryForSymType(
|
||||
symTyp: Class<out T>,
|
||||
paramTyp: Class<out P>? = null
|
||||
) = __findFactoryForSymType(symTyp = symTyp, paramTyp = paramTyp, fail = false)
|
||||
|
||||
/**
|
||||
* Get the index factory for the given symbol type failing if not found.
|
||||
*
|
||||
* @param symTyp The symbol type.
|
||||
* @param paramTyp The parameter type.
|
||||
* @return The index factory.
|
||||
*/
|
||||
fun <T : IIndexable, P : IIndexParams> getFactoryForSymType(
|
||||
symTyp: Class<out T>,
|
||||
paramTyp: Class<out P>? = null
|
||||
) = __findFactoryForSymType(symTyp = symTyp, paramTyp = paramTyp, fail = true)!!
|
||||
|
||||
/**
|
||||
* Finds an index factory for the given symbol type.
|
||||
*
|
||||
* @param symTyp The symbol type.
|
||||
* @param paramTyp The parameter type.
|
||||
* @param fail Whether to fail if no factory is found.
|
||||
* @return The index factory.
|
||||
*/
|
||||
@Suppress("UNCHECKED_CAST", "FunctionName")
|
||||
private fun <T : IIndexable, P : IIndexParams> __findFactoryForSymType(
|
||||
symTyp: Class<out T>,
|
||||
paramTyp: Class<out P>?,
|
||||
fail: Boolean = true
|
||||
): IIndexFactory<T, P>? {
|
||||
var factory: IIndexFactory<T, P>? = this.factoryCache[symTyp] as IIndexFactory<T, P>?
|
||||
if (factory != null) {
|
||||
return factory
|
||||
}
|
||||
|
||||
val impls = ServiceLoader.load(symTyp).iterator()
|
||||
while (impls.hasNext()) {
|
||||
val impl = impls.next()
|
||||
if (impl is IIndexFactory<*, *> && symTyp == impl.indexableType()) {
|
||||
if (factory == null) {
|
||||
factory = impl as IIndexFactory<T, P>
|
||||
continue
|
||||
}
|
||||
|
||||
if (!fail) {
|
||||
return null
|
||||
}
|
||||
|
||||
throw MultipleFactoriesException(symTyp)
|
||||
}
|
||||
}
|
||||
|
||||
if (factory != null && paramTyp != null && factory.paramType() != paramTyp) {
|
||||
if (!fail) {
|
||||
return null
|
||||
}
|
||||
throw InvalidParamTypeException(factory, paramTyp)
|
||||
}
|
||||
|
||||
if (factory == null) {
|
||||
if (!fail) {
|
||||
return null
|
||||
}
|
||||
|
||||
throw NotFoundException(symTyp)
|
||||
}
|
||||
|
||||
val typ = factory.indexableType()
|
||||
|
||||
factoryCache.put(typ, factory)?.also {
|
||||
throw IllegalStateException("Invalid cache state")
|
||||
}
|
||||
|
||||
return factory
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Exception thrown when an [IIndexFactory] cannot be found.
|
||||
*/
|
||||
class NotFoundException(sym: Class<out IIndexable>) :
|
||||
RuntimeException("No index factory found for ${sym.name}")
|
||||
|
||||
/**
|
||||
* Exception thrown when multiple [IIndexFactory]s are found for a single symbol type.
|
||||
*/
|
||||
class MultipleFactoriesException(sym: Class<out IIndexable>) :
|
||||
RuntimeException("Multiple index factories found for ${sym.name}")
|
||||
|
||||
/**
|
||||
* Exception thrown when the index parameters of a factory are of invalid type.
|
||||
*/
|
||||
class InvalidParamTypeException(factory: IIndexFactory<*, *>, paramTyp: Class<out IIndexParams>) :
|
||||
RuntimeException("Factory ${factory.javaClass} expected parameters of type ${factory.paramType()} but an instance of $paramTyp was provided")
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* 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
|
||||
|
||||
/**
|
||||
* Index configuration
|
||||
*
|
||||
* @author Akash Yadav
|
||||
*/
|
||||
interface IIndexParams
|
||||
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* 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 com.itsaky.androidide.progress.IProgressIndicator
|
||||
import com.itsaky.androidide.projects.api.Project
|
||||
import kotlinx.coroutines.Deferred
|
||||
import java.io.File
|
||||
|
||||
/**
|
||||
* An index service is responsible for indexing symbols.
|
||||
*
|
||||
* @author Akash Yadav
|
||||
*/
|
||||
interface IIndexService {
|
||||
|
||||
/**
|
||||
* Called to scan files that need to be indexed by this index service.
|
||||
*
|
||||
* @param project The root project model which can be used to query the project properties.
|
||||
* @return A [Deferred] collection of files that need to be indexed.
|
||||
*/
|
||||
fun scanFiles(project: Project): Collection<File>
|
||||
|
||||
/**
|
||||
* Called to index the given files.
|
||||
*
|
||||
* @param project The root project model which can be used to query the project properties.
|
||||
* @param progress An [IProgressIndicator] to report progress of the indexing process.
|
||||
* @param files The files to index.
|
||||
*/
|
||||
suspend fun indexFiles(project: Project, progress: IProgressIndicator, files: Collection<File>)
|
||||
}
|
||||
+4
-21
@@ -17,32 +17,15 @@
|
||||
|
||||
package com.itsaky.androidide.indexing
|
||||
|
||||
import kotlinx.coroutines.Deferred
|
||||
|
||||
/**
|
||||
* An indexer is responsible for indexing symbols.
|
||||
* Parameters for [IIndexService].
|
||||
*
|
||||
* @author Akash Yadav
|
||||
*/
|
||||
interface IIndexer<IndexableT : IIndexable, ReturnT> : AutoCloseable {
|
||||
interface IIndexerParams<ParamsT : IIndexParams> {
|
||||
|
||||
/**
|
||||
* The path of the index. This does not represent the actual path in the file system, but rather the path
|
||||
* that will be used to uniquely identify the index.
|
||||
* Parameters for the index.
|
||||
*/
|
||||
val indexPath: String
|
||||
|
||||
/**
|
||||
* Perform the indexing in the given index.
|
||||
*
|
||||
* @param index The index to perform the indexing in.
|
||||
* @param params The parameters for the indexing.
|
||||
* @return The collection of deferred results.
|
||||
*/
|
||||
fun index(index: IIndex<IndexableT>, params: Params): Collection<Deferred<ReturnT>>
|
||||
|
||||
/**
|
||||
* The parameters for the indexer.
|
||||
*/
|
||||
interface Params
|
||||
val indexParams: ParamsT
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
/build
|
||||
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* 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")
|
||||
}
|
||||
|
||||
groupConfig {
|
||||
groupIdSuffix.set("core")
|
||||
}
|
||||
|
||||
android {
|
||||
namespace = "${BuildConfig.packageName}.indexing.core"
|
||||
}
|
||||
|
||||
dependencies {
|
||||
kapt(libs.google.auto.service)
|
||||
implementation(libs.google.auto.service.annotations)
|
||||
|
||||
api(libs.composite.jaxp)
|
||||
|
||||
api(projects.core.common)
|
||||
api(projects.core.indexingApi)
|
||||
api(projects.logging.logger)
|
||||
api(projects.utilities.shared)
|
||||
|
||||
testImplementation(projects.testing.unitTest)
|
||||
}
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* 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.internal.platform
|
||||
|
||||
import com.google.auto.service.AutoService
|
||||
import com.itsaky.androidide.indexing.IIndex
|
||||
import com.itsaky.androidide.indexing.IIndexFactory
|
||||
import com.itsaky.androidide.indexing.core.platform.AbstractIndexFactory
|
||||
import com.itsaky.androidide.indexing.core.platform.ClassOrMemberInfo
|
||||
import com.itsaky.androidide.indexing.core.platform.IApiVersionsIndex.Params
|
||||
|
||||
/**
|
||||
* @author Akash Yadav
|
||||
*/
|
||||
@AutoService(IIndexFactory::class)
|
||||
internal class ApiVersionsIndexFactory : AbstractIndexFactory<ClassOrMemberInfo, Params>() {
|
||||
|
||||
override fun indexableType(): Class<ClassOrMemberInfo> {
|
||||
return ClassOrMemberInfo::class.java
|
||||
}
|
||||
|
||||
override fun paramType(): Class<out Params> {
|
||||
return Params::class.java
|
||||
}
|
||||
|
||||
override fun create(): IIndex<ClassOrMemberInfo, Params> {
|
||||
return ApiVersionsIndexImpl.newInstance(params)
|
||||
}
|
||||
}
|
||||
+51
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* 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.internal.platform
|
||||
|
||||
import com.itsaky.androidide.db.IRealmProvider
|
||||
import com.itsaky.androidide.indexing.AbstractDBIndex
|
||||
import com.itsaky.androidide.indexing.core.platform.ClassOrMemberInfo
|
||||
import com.itsaky.androidide.indexing.core.platform.IApiVersionsIndex
|
||||
import com.itsaky.androidide.indexing.core.platform.IPlatformIndex
|
||||
import io.realm.Realm
|
||||
|
||||
/**
|
||||
* Index to store [ClassOrMemberInfo].
|
||||
*
|
||||
* @author Akash Yadav
|
||||
*/
|
||||
internal class ApiVersionsIndexImpl private constructor(params: IApiVersionsIndex.Params?) :
|
||||
AbstractDBIndex<ClassOrMemberInfo, IApiVersionsIndex.Params>(params),
|
||||
IApiVersionsIndex {
|
||||
|
||||
companion object {
|
||||
private const val NAME = "member-info"
|
||||
private val PATH = IRealmProvider.createPath(IPlatformIndex.PLATFORM_INDEX_BASE_PATH, NAME)
|
||||
|
||||
@JvmStatic
|
||||
fun newInstance(params: IApiVersionsIndex.Params?) = ApiVersionsIndexImpl(params)
|
||||
}
|
||||
|
||||
override val name: String = NAME
|
||||
override val path: String = PATH
|
||||
|
||||
override val realm: Realm by lazy {
|
||||
val provider = IRealmProvider.instance()
|
||||
provider.get(path)
|
||||
}
|
||||
}
|
||||
+44
-44
@@ -15,9 +15,10 @@
|
||||
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.itsaky.androidide.lsp.java.indexing.apiinfo
|
||||
package com.itsaky.androidide.indexing.core.internal.platform
|
||||
|
||||
import androidx.collection.mutableIntObjectMapOf
|
||||
import com.itsaky.androidide.indexing.core.platform.ApiVersion
|
||||
import jaxp.xml.namespace.QName
|
||||
import jaxp.xml.stream.XMLInputFactory
|
||||
import jaxp.xml.stream.events.Attribute
|
||||
@@ -27,20 +28,26 @@ import org.slf4j.LoggerFactory
|
||||
import java.io.InputStream
|
||||
|
||||
/**
|
||||
* Parser for parsing `api-versions.xml` file from the Android SDK and building [ApiInfo] models.
|
||||
* Parser for parsing `api-versions.xml` file from the Android SDK and building [ApiVersion] models.
|
||||
*
|
||||
* @author Akash Yadav
|
||||
*/
|
||||
class ApiInfoParser {
|
||||
open class ApiVersionsParser {
|
||||
|
||||
private val apiInfos = mutableIntObjectMapOf<ApiInfo>()
|
||||
private val apiInfo = HashMap<String, Pair<ApiInfo, HashMap<String, ApiInfo>>>()
|
||||
// A cache to store the ApiVersion instances with different values
|
||||
// For example, if two classes have the same value for `since`,
|
||||
// 'removed' and 'deprecated', then the same ApiVersion instance
|
||||
// will be used for the two classes.
|
||||
// The keys in this map are the packed (bitwise ORed) values of the three version integers.
|
||||
// The first 8 bits are 0, the next 8 bits represent 'since', the next 8 bits represent 'deprecated'
|
||||
// and the last 8 bits represent 'removed'.
|
||||
private val apiInfosCache = mutableIntObjectMapOf<ApiVersion>()
|
||||
|
||||
private var apiVersion: Int? = null
|
||||
private var currentClass: String? = null
|
||||
|
||||
companion object {
|
||||
private val log = LoggerFactory.getLogger(ApiInfoParser::class.java)
|
||||
private val log = LoggerFactory.getLogger(ApiVersionsParser::class.java)
|
||||
}
|
||||
|
||||
fun parse(apiVersionsXml: InputStream) {
|
||||
@@ -65,32 +72,6 @@ class ApiInfoParser {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns [ApiInfo] for the given class.
|
||||
*/
|
||||
fun getClassInfo(className: String): ApiInfo? {
|
||||
return apiInfo[className]?.first
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns [ApiInfo] for the given class and member.
|
||||
*/
|
||||
fun getMemberInfo(className: String, memberName: String): ApiInfo? {
|
||||
return apiInfo[className]?.second?.get(memberName)
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes and returns the [ApiInfo] for the given class and member. This also removes the [ApiInfo]
|
||||
* for the class if the all the members of the class have been removed.
|
||||
*/
|
||||
fun removeMemberInfo(className: String, memberName: String): ApiInfo? {
|
||||
return apiInfo[className]?.second?.remove(memberName).also {
|
||||
if (apiInfo[className]?.second?.isEmpty() == true) {
|
||||
apiInfo.remove(className)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun consumeStartElement(event: StartElement) {
|
||||
when (event.name.localPart) {
|
||||
"api" -> apiVersion = event.getAttributeByName(QName("version")).value.toInt()
|
||||
@@ -110,32 +91,51 @@ class ApiInfoParser {
|
||||
}
|
||||
|
||||
val (name, versions) = event.parseAttrs()
|
||||
check(!apiInfo.containsKey(name)) {
|
||||
check(!isDuplicateClass(name)) {
|
||||
"Duplicate class entry: $name"
|
||||
}
|
||||
|
||||
val apiInfo = apiInfos.getOrPut(versions) {
|
||||
val apiInfo = apiInfosCache.getOrPut(versions) {
|
||||
createApiInfo(versions)
|
||||
}
|
||||
|
||||
this.apiInfo[name] = apiInfo to HashMap()
|
||||
consumeClassVersionInfo(name, apiInfo)
|
||||
this.currentClass = name
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the given class name is a duplicate.
|
||||
*/
|
||||
protected open fun isDuplicateClass(name: String): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
protected open fun consumeClassVersionInfo(name: String, apiVersion: ApiVersion) {}
|
||||
|
||||
private fun consumeMember(event: StartElement, memberType: String) {
|
||||
val (name, versions) = event.parseAttrs()
|
||||
val currentClass = checkNotNull(currentClass) {
|
||||
"<${memberType}> element must be inside <class> element"
|
||||
}
|
||||
check(!apiInfo.containsKey(name)) {
|
||||
check(!isDuplicateMember(currentClass, name)) {
|
||||
"Duplicate $memberType entry in class $currentClass: $name"
|
||||
}
|
||||
consumeMemberVersionInfo(currentClass, name, memberType, createApiInfo(versions))
|
||||
}
|
||||
|
||||
val (_, members) = apiInfo[currentClass]!!
|
||||
val existing = members.put(name, createApiInfo(versions))
|
||||
check(existing == null) {
|
||||
"Duplicate $memberType entry in class $currentClass: $name"
|
||||
}
|
||||
/**
|
||||
* Check if the given member of the given class is a duplicate.
|
||||
*/
|
||||
protected open fun isDuplicateMember(className: String, memberName: String): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
protected open fun consumeMemberVersionInfo(
|
||||
className: String,
|
||||
member: String,
|
||||
memberType: String,
|
||||
apiVersion: ApiVersion
|
||||
) {
|
||||
}
|
||||
|
||||
private fun consumeEndElement(element: EndElement) {
|
||||
@@ -171,14 +171,14 @@ class ApiInfoParser {
|
||||
"Invalid version: $since, $deprecated, $removed"
|
||||
}
|
||||
|
||||
val version = (since shl 16) + (deprecated shl 8) + removed
|
||||
val version = (since shl 16) or (deprecated shl 8) or removed
|
||||
return name!! to version
|
||||
}
|
||||
|
||||
private fun createApiInfo(versions: Int): ApiInfo {
|
||||
private fun createApiInfo(versions: Int): ApiVersion {
|
||||
val since = (versions shr 16) and 0x000000FF
|
||||
val deprecated = (versions shr 8) and 0x000000FF
|
||||
val removed = versions and 0x000000FF
|
||||
return ApiInfo.newInstance(since = since, deprecatedIn = deprecated, removedIn = removed)
|
||||
return ApiVersion.newInstance(since = since, deprecatedIn = deprecated, removedIn = removed)
|
||||
}
|
||||
}
|
||||
+47
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* 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.internal.platform
|
||||
|
||||
import com.google.auto.service.AutoService
|
||||
import com.itsaky.androidide.indexing.IIndexService
|
||||
import com.itsaky.androidide.progress.IProgressIndicator
|
||||
import com.itsaky.androidide.projects.api.Project
|
||||
import java.io.File
|
||||
|
||||
/**
|
||||
* @author Akash Yadav
|
||||
*/
|
||||
@AutoService(IIndexService::class)
|
||||
internal class PlatformIndexService : IIndexService {
|
||||
|
||||
override fun scanFiles(project: Project): Collection<File> {
|
||||
return mutableListOf<File>().apply {
|
||||
project.findAndroidModules().forEach { androidModule ->
|
||||
add(androidModule.getPlatformDir() ?: return@forEach)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun indexFiles(
|
||||
project: Project,
|
||||
progress: IProgressIndicator,
|
||||
files: Collection<File>
|
||||
) {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
}
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* 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.platform
|
||||
|
||||
import com.itsaky.androidide.indexing.IIndexFactory
|
||||
import com.itsaky.androidide.indexing.IIndexParams
|
||||
import com.itsaky.androidide.indexing.IIndexable
|
||||
|
||||
/**
|
||||
* Abstract implementation of the [IIndexFactory].
|
||||
*
|
||||
* @author Akash Yadav
|
||||
*/
|
||||
abstract class AbstractIndexFactory<I : IIndexable, P : IIndexParams> : IIndexFactory<I, P> {
|
||||
|
||||
override var params: P? = null
|
||||
}
|
||||
+16
-16
@@ -15,11 +15,11 @@
|
||||
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.itsaky.androidide.lsp.java.indexing.apiinfo
|
||||
package com.itsaky.androidide.indexing.core.platform
|
||||
|
||||
import com.google.common.base.Objects
|
||||
import com.itsaky.androidide.lsp.java.indexing.ICloneable
|
||||
import com.itsaky.androidide.lsp.java.indexing.ISharedJavaIndexable
|
||||
import com.itsaky.androidide.indexing.IIndexable
|
||||
import com.itsaky.androidide.models.ICloneable
|
||||
import io.realm.annotations.PrimaryKey
|
||||
import io.realm.annotations.RealmClass
|
||||
import io.realm.annotations.RealmField
|
||||
@@ -33,12 +33,12 @@ import io.realm.annotations.Required
|
||||
*/
|
||||
|
||||
@RealmClass
|
||||
open class ApiInfo : ISharedJavaIndexable, ICloneable {
|
||||
open class ApiVersion : IIndexable, ICloneable {
|
||||
|
||||
@Required
|
||||
@PrimaryKey
|
||||
@RealmField("id")
|
||||
override var id: Int? = null
|
||||
var id: Int? = null
|
||||
|
||||
@RealmField("since")
|
||||
var since: Int = 1
|
||||
@@ -52,17 +52,17 @@ open class ApiInfo : ISharedJavaIndexable, ICloneable {
|
||||
var removedIn: Int = 0
|
||||
private set
|
||||
|
||||
override fun computeId() {
|
||||
private fun computeId() {
|
||||
this.id = Objects.hashCode(this.since, this.removedIn, this.deprecatedIn)
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the API information from the given [ApiInfo].
|
||||
* Update the API information from the given [ApiVersion].
|
||||
*/
|
||||
fun update(apiInfo: ApiInfo) = apply {
|
||||
this.since = apiInfo.since
|
||||
this.deprecatedIn = apiInfo.deprecatedIn
|
||||
this.removedIn = apiInfo.removedIn
|
||||
fun update(apiVersion: ApiVersion) = apply {
|
||||
this.since = apiVersion.since
|
||||
this.deprecatedIn = apiVersion.deprecatedIn
|
||||
this.removedIn = apiVersion.removedIn
|
||||
this.computeId()
|
||||
}
|
||||
|
||||
@@ -82,7 +82,7 @@ open class ApiInfo : ISharedJavaIndexable, ICloneable {
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (other !is ApiInfo) return false
|
||||
if (other !is ApiVersion) return false
|
||||
|
||||
if (id != other.id) return false
|
||||
if (since != other.since) return false
|
||||
@@ -101,17 +101,17 @@ open class ApiInfo : ISharedJavaIndexable, ICloneable {
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
return "ApiInfo(id=$id, since=$since, deprecatedIn=$deprecatedIn, removedIn=$removedIn)"
|
||||
return "ApiVersion(id=$id, since=$since, deprecatedIn=$deprecatedIn, removedIn=$removedIn)"
|
||||
}
|
||||
|
||||
override fun clone(): ApiInfo {
|
||||
override fun clone(): ApiVersion {
|
||||
return newInstance(since, deprecatedIn, removedIn)
|
||||
}
|
||||
|
||||
companion object {
|
||||
@JvmStatic
|
||||
fun newInstance(since: Int, deprecatedIn: Int, removedIn: Int): ApiInfo {
|
||||
return ApiInfo().apply {
|
||||
fun newInstance(since: Int, deprecatedIn: Int, removedIn: Int): ApiVersion {
|
||||
return ApiVersion().apply {
|
||||
this.since = since
|
||||
this.deprecatedIn = deprecatedIn
|
||||
this.removedIn = removedIn
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* 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.platform
|
||||
|
||||
import io.realm.annotations.RealmField
|
||||
import io.realm.annotations.Required
|
||||
|
||||
/**
|
||||
* [ApiVersion] of a class or class members.
|
||||
*
|
||||
* @property className The fully qualified name of the class.
|
||||
* @property memberName The name of the member (optional). Must be set if the objects represents API
|
||||
* information about a member of a class. Otherwise, it could be `null`.
|
||||
* @property apiVersion The [ApiVersion] about the class or the member.
|
||||
*
|
||||
* @author Akash Yadav
|
||||
*/
|
||||
open class ClassOrMemberInfo : IPlatformIndexable {
|
||||
|
||||
@Required
|
||||
@RealmField("className")
|
||||
var className: String? = null
|
||||
|
||||
@Required
|
||||
@RealmField("memberName")
|
||||
var memberName: String? = null
|
||||
|
||||
@Required
|
||||
@RealmField("apiVersion")
|
||||
var apiVersion: ApiVersion? = null
|
||||
|
||||
fun isMember(): Boolean = !isClass()
|
||||
fun isClass(): Boolean = this.memberName == null
|
||||
}
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* 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.platform
|
||||
|
||||
import com.itsaky.androidide.indexing.IIndexParams
|
||||
import com.itsaky.androidide.indexing.core.platform.IApiVersionsIndex.Params
|
||||
|
||||
/**
|
||||
* Index which provides access to the API versions informations (`api-versions.xml`) from the
|
||||
* Android SDK.
|
||||
*
|
||||
* @author Akash Yadav
|
||||
*/
|
||||
interface IApiVersionsIndex : IPlatformIndex<ClassOrMemberInfo, Params> {
|
||||
|
||||
class Params(
|
||||
val platformId: String
|
||||
) : IIndexParams
|
||||
}
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* 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.platform
|
||||
|
||||
import com.itsaky.androidide.db.IRealmProvider
|
||||
import com.itsaky.androidide.indexing.IIndex
|
||||
import com.itsaky.androidide.indexing.IIndexParams
|
||||
|
||||
/**
|
||||
* Index for data available in the Android Platform SDK.
|
||||
*
|
||||
* @author Akash Yadav
|
||||
*/
|
||||
interface IPlatformIndex<T : IPlatformIndexable, C : IIndexParams> : IIndex<T, C> {
|
||||
|
||||
companion object {
|
||||
|
||||
/**
|
||||
* Base path for the platform index.
|
||||
*/
|
||||
val PLATFORM_INDEX_BASE_PATH = IRealmProvider.createPath(IIndex.INDEX_BASE_PATH, "platform")
|
||||
}
|
||||
}
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* 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.platform
|
||||
|
||||
import com.itsaky.androidide.indexing.IIndexable
|
||||
|
||||
/**
|
||||
* Symbols that are indexed in the [IPlatformIndex].
|
||||
*
|
||||
* @author Akash Yadav
|
||||
*/
|
||||
interface IPlatformIndexable : IIndexable
|
||||
+18
-15
@@ -15,7 +15,7 @@
|
||||
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.itsaky.androidide.lsp.java.indexing.apiinfo
|
||||
package com.itsaky.androidide.indexing.core.platform
|
||||
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import com.itsaky.androidide.testing.common.utils.findAndroidHome
|
||||
@@ -32,7 +32,7 @@ import java.io.File
|
||||
* @author Akash Yadav
|
||||
*/
|
||||
@RunWith(RobolectricTestRunner::class)
|
||||
class ApiInfoParserTest {
|
||||
class ApiVersionParserTest {
|
||||
|
||||
val apiVersionXml by lazy {
|
||||
val platformsDir = File(findAndroidHome(), "platforms")
|
||||
@@ -44,11 +44,11 @@ class ApiInfoParserTest {
|
||||
platformDir.resolve("data/api-versions.xml")
|
||||
}
|
||||
|
||||
private lateinit var parser: ApiInfoParser
|
||||
private lateinit var parser: CollectingApiVersionsParser
|
||||
|
||||
@Before
|
||||
fun setup() {
|
||||
parser = ApiInfoParser()
|
||||
parser = CollectingApiVersionsParser()
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -71,7 +71,7 @@ class ApiInfoParserTest {
|
||||
""".trimIndent()
|
||||
val inputStream = ByteArrayInputStream(xml.toByteArray())
|
||||
|
||||
val parser = ApiInfoParser()
|
||||
val parser = CollectingApiVersionsParser()
|
||||
parser.parse(inputStream)
|
||||
|
||||
val classInfo = parser.getClassInfo("com.example.ClassA")
|
||||
@@ -93,7 +93,7 @@ class ApiInfoParserTest {
|
||||
""".trimIndent()
|
||||
val inputStream = ByteArrayInputStream(xml.toByteArray())
|
||||
|
||||
val parser = ApiInfoParser()
|
||||
val parser = CollectingApiVersionsParser()
|
||||
parser.parse(inputStream)
|
||||
|
||||
val classInfo = parser.getClassInfo("com.example.ClassA")
|
||||
@@ -119,7 +119,7 @@ class ApiInfoParserTest {
|
||||
""".trimIndent()
|
||||
val inputStream = ByteArrayInputStream(xml.toByteArray())
|
||||
|
||||
val parser = ApiInfoParser()
|
||||
val parser = CollectingApiVersionsParser()
|
||||
parser.parse(inputStream)
|
||||
|
||||
val classAInfo = parser.getClassInfo("com.example.ClassA")
|
||||
@@ -140,7 +140,7 @@ class ApiInfoParserTest {
|
||||
""".trimIndent()
|
||||
val inputStream = ByteArrayInputStream(xml.toByteArray())
|
||||
|
||||
val parser = ApiInfoParser()
|
||||
val parser = CollectingApiVersionsParser()
|
||||
parser.parse(inputStream)
|
||||
|
||||
val classInfo = parser.getClassInfo("com.example.ClassA")
|
||||
@@ -159,7 +159,7 @@ class ApiInfoParserTest {
|
||||
""".trimIndent()
|
||||
val inputStream = ByteArrayInputStream(xml.toByteArray())
|
||||
|
||||
val parser = ApiInfoParser()
|
||||
val parser = CollectingApiVersionsParser()
|
||||
try {
|
||||
parser.parse(inputStream)
|
||||
assertThat(true).isFalse() // Should not reach here
|
||||
@@ -177,7 +177,7 @@ class ApiInfoParserTest {
|
||||
""".trimIndent()
|
||||
val inputStream = ByteArrayInputStream(xml.toByteArray())
|
||||
|
||||
val parser = ApiInfoParser()
|
||||
val parser = CollectingApiVersionsParser()
|
||||
try {
|
||||
parser.parse(inputStream)
|
||||
assertThat(true).isFalse() // Should not reach here
|
||||
@@ -196,7 +196,7 @@ class ApiInfoParserTest {
|
||||
""".trimIndent()
|
||||
val inputStream = ByteArrayInputStream(xml.toByteArray())
|
||||
|
||||
val parser = ApiInfoParser()
|
||||
val parser = CollectingApiVersionsParser()
|
||||
try {
|
||||
parser.parse(inputStream)
|
||||
assertThat(true).isFalse() // Should not reach here
|
||||
@@ -217,7 +217,7 @@ class ApiInfoParserTest {
|
||||
""".trimIndent()
|
||||
val inputStream = ByteArrayInputStream(xml.toByteArray())
|
||||
|
||||
val parser = ApiInfoParser()
|
||||
val parser = CollectingApiVersionsParser()
|
||||
try {
|
||||
parser.parse(inputStream)
|
||||
assertThat(true).isFalse() // Should not reach here
|
||||
@@ -238,8 +238,11 @@ class ApiInfoParserTest {
|
||||
""".trimIndent()
|
||||
val inputStream = ByteArrayInputStream(xml.toByteArray())
|
||||
|
||||
val parser = ApiInfoParser()
|
||||
assertThrows("The markup in the document following the root element must be well-formed.", XMLStreamException::class.java) {
|
||||
val parser = CollectingApiVersionsParser()
|
||||
assertThrows(
|
||||
"The markup in the document following the root element must be well-formed.",
|
||||
XMLStreamException::class.java
|
||||
) {
|
||||
parser.parse(inputStream)
|
||||
}
|
||||
}
|
||||
@@ -328,7 +331,7 @@ class ApiInfoParserTest {
|
||||
xmlBuilder.append("</api>")
|
||||
val inputStream = ByteArrayInputStream(xmlBuilder.toString().toByteArray())
|
||||
|
||||
val parser = ApiInfoParser()
|
||||
val parser = CollectingApiVersionsParser()
|
||||
parser.parse(inputStream)
|
||||
|
||||
for (i in 1..1000) {
|
||||
+79
@@ -0,0 +1,79 @@
|
||||
/*
|
||||
* 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.platform
|
||||
|
||||
import com.itsaky.androidide.indexing.core.internal.platform.ApiVersionsParser
|
||||
|
||||
/**
|
||||
* @author Akash Yadav
|
||||
*/
|
||||
class CollectingApiVersionsParser : ApiVersionsParser() {
|
||||
|
||||
private val apiInfo = mutableMapOf<String, Pair<ApiVersion, MutableMap<String, ApiVersion>>>()
|
||||
|
||||
override fun isDuplicateClass(name: String): Boolean {
|
||||
return apiInfo.containsKey(name)
|
||||
}
|
||||
|
||||
override fun consumeClassVersionInfo(name: String, apiVersion: ApiVersion) {
|
||||
this.apiInfo[name] = apiVersion to HashMap()
|
||||
}
|
||||
|
||||
override fun isDuplicateMember(className: String, memberName: String): Boolean {
|
||||
return apiInfo[className]?.second?.containsKey(memberName) == true
|
||||
}
|
||||
|
||||
override fun consumeMemberVersionInfo(
|
||||
className: String,
|
||||
member: String,
|
||||
memberType: String,
|
||||
apiVersion: ApiVersion
|
||||
) {
|
||||
val (_, members) = apiInfo[className]!!
|
||||
val existing = members.put(member, apiVersion)
|
||||
check(existing == null) {
|
||||
"Duplicate $memberType entry in class $className: $member"
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns [ApiVersion] for the given class.
|
||||
*/
|
||||
fun getClassInfo(className: String): ApiVersion? {
|
||||
return apiInfo[className]?.first
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns [ApiVersion] for the given class and member.
|
||||
*/
|
||||
fun getMemberInfo(className: String, memberName: String): ApiVersion? {
|
||||
return apiInfo[className]?.second?.get(memberName)
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes and returns the [ApiVersion] for the given class and member. This also removes the [ApiVersion]
|
||||
* for the class if the all the members of the class have been removed.
|
||||
*/
|
||||
fun removeMemberInfo(className: String, memberName: String): ApiVersion? {
|
||||
return apiInfo[className]?.second?.remove(memberName).also {
|
||||
if (apiInfo[className]?.second?.isEmpty() == true) {
|
||||
apiInfo.remove(className)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,71 +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 com.itsaky.androidide.db.IRealmProvider
|
||||
import com.itsaky.androidide.db.IRealmProvider.Companion.PATH_SEPARATOR
|
||||
import com.itsaky.androidide.indexing.internal.DefaultIndex
|
||||
import io.realm.RealmConfiguration
|
||||
|
||||
/**
|
||||
* The [IndexProvider] provides APIs to index symbols in a database.
|
||||
*
|
||||
* @author itsaky
|
||||
*/
|
||||
object IndexProvider {
|
||||
|
||||
const val INDEX_BASE_PATH = "${PATH_SEPARATOR}indexes"
|
||||
|
||||
/**
|
||||
* Get the index for the given indexable type in the root path.
|
||||
*
|
||||
* @param T The indexable type.
|
||||
* @param config The index configuration.
|
||||
* @param name The internal path of the index.
|
||||
*/
|
||||
fun <T : IIndexable> getRootIndex(
|
||||
name: String,
|
||||
config: IndexConfig? = null
|
||||
): IIndex<T> {
|
||||
require(name.indexOf(PATH_SEPARATOR) == -1)
|
||||
return getIndexForPath("${PATH_SEPARATOR}${name}", config)
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the index for the given indexable type.
|
||||
*
|
||||
* @param path The internal path of the index.
|
||||
* @param config The index configuration.
|
||||
*/
|
||||
fun <T : IIndexable> getIndexForPath(
|
||||
path: String,
|
||||
config: IndexConfig? = null
|
||||
): IIndex<T> {
|
||||
val sepIdx = path.indexOfLast { it == PATH_SEPARATOR }
|
||||
val name = if (sepIdx > 0) path.substring(sepIdx + 1) else path
|
||||
val provider = IRealmProvider.instance()
|
||||
val realm = provider.get("${INDEX_BASE_PATH}${path}", config)
|
||||
val index = DefaultIndex.create<T>(name, path, realm)
|
||||
return index
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The [IndexConfig] is used to configure the index.
|
||||
*/
|
||||
typealias IndexConfig = RealmConfiguration.Builder.() -> Unit
|
||||
@@ -1,76 +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.internal
|
||||
|
||||
import com.itsaky.androidide.indexing.IIndex
|
||||
import com.itsaky.androidide.indexing.IIndexable
|
||||
import com.itsaky.androidide.indexing.IndexConfig
|
||||
import com.itsaky.androidide.indexing.IndexProvider
|
||||
import io.realm.Realm
|
||||
import org.slf4j.LoggerFactory
|
||||
|
||||
/**
|
||||
* Default implementation of [IIndex].
|
||||
*
|
||||
* @author Akash Yadav
|
||||
*/
|
||||
class DefaultIndex<IndexableT : IIndexable> private constructor(
|
||||
override val name: String,
|
||||
override val path: String,
|
||||
private val realm: Realm,
|
||||
) : IIndex<IndexableT> {
|
||||
|
||||
companion object {
|
||||
private val log = LoggerFactory.getLogger(DefaultIndex::class.java)
|
||||
const val DEFAULT_PUT_BATCH_SIZE = 100
|
||||
|
||||
/**
|
||||
* Creates a new [IIndex] instance.
|
||||
*/
|
||||
fun <IndexableT : IIndexable> create(
|
||||
name: String,
|
||||
path: String,
|
||||
realm: Realm,
|
||||
): IIndex<IndexableT> {
|
||||
return DefaultIndex(name, path, realm)
|
||||
}
|
||||
}
|
||||
|
||||
// override fun query(condition: QueryCondition<IndexableT>): Query<IndexableT> {
|
||||
// return box.query(condition).build()
|
||||
// }
|
||||
|
||||
override fun index(symbol: IndexableT, async: Boolean) {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
override fun indexBatched(symbols: Collection<IndexableT>, batchSize: Int, async: Boolean) {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
override fun <I : IIndexable> createSubIndex(
|
||||
name: String,
|
||||
config: IndexConfig?
|
||||
): IIndex<I> {
|
||||
return IndexProvider.getIndexForPath(IIndex.createIndexPath(this.path, name), config)
|
||||
}
|
||||
|
||||
override fun delete() {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
}
|
||||
@@ -113,8 +113,10 @@ class ProjectManagerImpl : IProjectManager, EventReceiver {
|
||||
androidBuildVariants = buildVariants
|
||||
}
|
||||
|
||||
log.info("Found {} project sync issues: {}", rootProject.projectSyncIssues.syncIssues.size,
|
||||
rootProject.projectSyncIssues.syncIssues)
|
||||
log.info(
|
||||
"Found {} project sync issues: {}", rootProject.projectSyncIssues.syncIssues.size,
|
||||
rootProject.projectSyncIssues.syncIssues
|
||||
)
|
||||
|
||||
withStopWatch("Setup project") {
|
||||
val indexerScope = CoroutineScope(Dispatchers.Default)
|
||||
@@ -227,7 +229,8 @@ class ProjectManagerImpl : IProjectManager, EventReceiver {
|
||||
val variant = module.getSelectedVariant()
|
||||
if (variant == null) {
|
||||
log.error(
|
||||
"Selected build variant for project '{}' not found", module.path)
|
||||
"Selected build variant for project '{}' not found", module.path
|
||||
)
|
||||
return@flatMap emptyList()
|
||||
}
|
||||
|
||||
|
||||
@@ -163,8 +163,10 @@ open class AndroidModule( // Class must be open because BaseXMLTest mocks this..
|
||||
|
||||
override fun getSourceDirectories(): Set<File> {
|
||||
if (mainSourceSet == null) {
|
||||
log.warn("No main source set is available for project {}. Cannot get source directories.",
|
||||
name)
|
||||
log.warn(
|
||||
"No main source set is available for project {}. Cannot get source directories.",
|
||||
name
|
||||
)
|
||||
return mutableSetOf()
|
||||
}
|
||||
|
||||
@@ -256,7 +258,8 @@ open class AndroidModule( // Class must be open because BaseXMLTest mocks this..
|
||||
// Read resources in parallel
|
||||
withStopWatch("Read resources for module : $path") {
|
||||
val resourceReaderScope = CoroutineScope(
|
||||
Dispatchers.IO + CoroutineName("ResourceReader($path)"))
|
||||
Dispatchers.IO + CoroutineName("ResourceReader($path)")
|
||||
)
|
||||
|
||||
val resourceFlow = flow {
|
||||
emit(getFrameworkResourceTable())
|
||||
@@ -361,8 +364,8 @@ open class AndroidModule( // Class must be open because BaseXMLTest mocks this..
|
||||
libraryMap.values
|
||||
.filter { library ->
|
||||
library.type == ANDROID_LIBRARY &&
|
||||
library.androidLibraryData!!.resFolder.exists() &&
|
||||
library.findPackageName() != UNKNOWN_PACKAGE
|
||||
library.androidLibraryData!!.resFolder.exists() &&
|
||||
library.findPackageName() != UNKNOWN_PACKAGE
|
||||
}
|
||||
.also { libs -> deps = libs.size }
|
||||
.mapNotNull { library ->
|
||||
@@ -493,19 +496,26 @@ open class AndroidModule( // Class must be open because BaseXMLTest mocks this..
|
||||
val info = projectManager.androidBuildVariants[this.path]
|
||||
if (info == null) {
|
||||
log.error(
|
||||
"Failed to find selected build variant for module: '{}'", this.path)
|
||||
"Failed to find selected build variant for module: '{}'", this.path
|
||||
)
|
||||
return null
|
||||
}
|
||||
|
||||
val variant = this.getVariant(info.selectedVariant)
|
||||
if (variant == null) {
|
||||
log.error(
|
||||
"Build variant with name '{}' not found.", info.selectedVariant)
|
||||
"Build variant with name '{}' not found.", info.selectedVariant
|
||||
)
|
||||
return null
|
||||
}
|
||||
|
||||
return variant
|
||||
}
|
||||
|
||||
private fun getPlatformDir() = bootClassPaths.firstOrNull { it.name == "android.jar" }?.parentFile
|
||||
/**
|
||||
* Get the Android SDK platform directory for this Android module.
|
||||
*
|
||||
* @return The Android SDK platform directory for this Android module, or `null` if none is found.
|
||||
*/
|
||||
fun getPlatformDir() = bootClassPaths.firstOrNull { it.name == "android.jar" }?.parentFile
|
||||
}
|
||||
|
||||
@@ -46,21 +46,24 @@ kapt {
|
||||
|
||||
dependencies {
|
||||
kapt(projects.annotation.processors)
|
||||
kapt(libs.google.auto.service)
|
||||
|
||||
implementation(libs.androidide.ts)
|
||||
implementation(libs.androidide.ts.java)
|
||||
implementation(libs.androidx.annotation)
|
||||
implementation(libs.androidx.appcompat)
|
||||
implementation(libs.common.editor)
|
||||
implementation(libs.common.javaparser)
|
||||
implementation(libs.common.utilcode)
|
||||
implementation(libs.androidx.annotation)
|
||||
|
||||
implementation(libs.google.auto.service.annotations)
|
||||
implementation(libs.google.guava)
|
||||
implementation(libs.google.gson)
|
||||
implementation(libs.google.material)
|
||||
|
||||
implementation(projects.core.actions)
|
||||
implementation(projects.core.common)
|
||||
implementation(projects.core.indexing)
|
||||
implementation(projects.core.indexingApi)
|
||||
implementation(projects.core.lspApi)
|
||||
implementation(projects.core.resources)
|
||||
implementation(projects.editor.api)
|
||||
|
||||
-1
@@ -20,7 +20,6 @@ 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.JavaIndexingRealmModule
|
||||
import com.itsaky.androidide.lsp.java.indexing.classfile.JavaType
|
||||
import com.itsaky.androidide.testing.android.rules.RealmDBTestRule
|
||||
import io.realm.RealmList
|
||||
|
||||
-1
@@ -22,7 +22,6 @@ import androidx.test.core.app.ApplicationProvider
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import com.blankj.utilcode.util.ConvertUtils
|
||||
import com.google.common.truth.Truth
|
||||
import com.itsaky.androidide.lsp.java.indexing.classfile.JavaIndexingRealmModule
|
||||
import com.itsaky.androidide.testing.android.rules.RealmDBTestRule
|
||||
import io.realm.RealmModel
|
||||
import org.junit.Rule
|
||||
|
||||
+3
-4
@@ -19,13 +19,12 @@ 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.apiinfo.ApiInfo
|
||||
import com.itsaky.androidide.indexing.core.platform.ApiInfo
|
||||
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.JavaConstant
|
||||
import com.itsaky.androidide.lsp.java.indexing.classfile.JavaIndexingRealmModule
|
||||
import com.itsaky.androidide.lsp.java.indexing.classfile.JavaType
|
||||
import com.itsaky.androidide.lsp.java.indexing.classfile.PrimitiveAnnotationElementValue
|
||||
import com.itsaky.androidide.testing.android.rules.RealmDBTestRule
|
||||
@@ -49,7 +48,7 @@ class SharedModelCRUDTest {
|
||||
@Test
|
||||
fun testApiInfoCreate() {
|
||||
dbTestRule.withDb("api-info") {
|
||||
val apiInfo = ApiInfo.newInstance(since = 1, deprecatedIn = 21, removedIn = 23)
|
||||
val apiInfo = com.itsaky.androidide.indexing.core.platform.ApiInfo.newInstance(since = 1, deprecatedIn = 21, removedIn = 23)
|
||||
assertInsertUnique(apiInfo)
|
||||
|
||||
val apiInfo2 = apiInfo.clone().update(removedIn = 26)
|
||||
@@ -63,7 +62,7 @@ class SharedModelCRUDTest {
|
||||
@Test(expected = RealmPrimaryKeyConstraintException::class)
|
||||
fun testApiInfoDuplicationError() {
|
||||
dbTestRule.withDb("api-info-duplication-error") {
|
||||
val apiInfo = ApiInfo.newInstance(since = 1, deprecatedIn = 21, removedIn = 23)
|
||||
val apiInfo = com.itsaky.androidide.indexing.core.platform.ApiInfo.newInstance(since = 1, deprecatedIn = 21, removedIn = 23)
|
||||
assertInsertSingle(apiInfo)
|
||||
assertInsertSingle(apiInfo)
|
||||
}
|
||||
|
||||
@@ -17,8 +17,6 @@
|
||||
|
||||
package com.itsaky.androidide.lsp.java.indexing
|
||||
|
||||
import com.itsaky.androidide.lsp.java.indexing.apiinfo.ApiInfo
|
||||
|
||||
/**
|
||||
* A java symbol.
|
||||
*
|
||||
@@ -28,5 +26,4 @@ import com.itsaky.androidide.lsp.java.indexing.apiinfo.ApiInfo
|
||||
*/
|
||||
interface IJavaSymbol : IJavaIndexable {
|
||||
var accessFlags: Int
|
||||
var apiInfo: ApiInfo?
|
||||
}
|
||||
@@ -1,56 +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.IIndex
|
||||
import com.itsaky.androidide.indexing.IIndexer
|
||||
import com.itsaky.androidide.indexing.IIndexer.Params
|
||||
import com.itsaky.androidide.tasks.cancelIfActive
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Deferred
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.async
|
||||
import java.io.File
|
||||
|
||||
/**
|
||||
* @author Akash Yadav
|
||||
*/
|
||||
class JavaIndexer(
|
||||
private val jarPaths: List<File>
|
||||
) : IIndexer<IJavaIndexable, Unit> {
|
||||
|
||||
companion object {
|
||||
const val INDEX_PATH = "/java"
|
||||
}
|
||||
|
||||
private val coroutineScope = CoroutineScope(Dispatchers.IO)
|
||||
override val indexPath: String = INDEX_PATH
|
||||
|
||||
override fun index(index: IIndex<IJavaIndexable>, params: Params): Collection<Deferred<Unit>> {
|
||||
return jarPaths.map { jar ->
|
||||
coroutineScope.async {
|
||||
val worker = JavaJarModelBuilder(jar)
|
||||
// worker.index(index)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun close() {
|
||||
coroutineScope.cancelIfActive("Cancellation requested")
|
||||
}
|
||||
}
|
||||
+15
-3
@@ -15,9 +15,22 @@
|
||||
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.itsaky.androidide.lsp.java.indexing.classfile
|
||||
package com.itsaky.androidide.lsp.java.indexing
|
||||
|
||||
import com.itsaky.androidide.lsp.java.indexing.apiinfo.ApiInfo
|
||||
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
|
||||
|
||||
/**
|
||||
@@ -33,7 +46,6 @@ import io.realm.annotations.RealmModule
|
||||
ClassAnnotationElementValue::class,
|
||||
EnumAnnotationElementValue::class,
|
||||
PrimitiveAnnotationElementValue::class,
|
||||
ApiInfo::class,
|
||||
JavaAnnotation::class,
|
||||
JavaClass::class,
|
||||
JavaConstant::class,
|
||||
+12
-11
@@ -269,17 +269,18 @@ class JavaJarModelBuilder(private val jar: File) {
|
||||
val methodName = method.getName(classFile.constant_pool)
|
||||
val methodType = DescriptorUtils.returnType(descriptor)
|
||||
|
||||
add(this@setAnnotationElements.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()
|
||||
}
|
||||
})
|
||||
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()
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+1
-7
@@ -18,7 +18,6 @@
|
||||
package com.itsaky.androidide.lsp.java.indexing.classfile
|
||||
|
||||
import com.itsaky.androidide.lsp.java.indexing.IJavaSymbol
|
||||
import com.itsaky.androidide.lsp.java.indexing.apiinfo.ApiInfo
|
||||
import io.realm.RealmAny
|
||||
import io.realm.RealmResults
|
||||
import io.realm.annotations.Index
|
||||
@@ -47,9 +46,6 @@ open class AnnotationElement : IJavaSymbol {
|
||||
@RealmField("accessFlags")
|
||||
override var accessFlags: Int = 0
|
||||
|
||||
@RealmField("apiInfo")
|
||||
override var apiInfo: ApiInfo? = null
|
||||
|
||||
@LinkingObjects("methods")
|
||||
val ofClass: RealmResults<JavaAnnotation>? = null
|
||||
|
||||
@@ -78,18 +74,16 @@ open class AnnotationElement : IJavaSymbol {
|
||||
|
||||
companion object {
|
||||
@JvmStatic
|
||||
fun JavaAnnotation.newAnnotationElement(
|
||||
fun newAnnotationElement(
|
||||
name: String,
|
||||
type: JavaType,
|
||||
accessFlags: Int,
|
||||
apiInfo: ApiInfo? = null,
|
||||
defaultValue: RealmAny? = null
|
||||
): AnnotationElement {
|
||||
return AnnotationElement().apply {
|
||||
this.name = name
|
||||
this.type = type
|
||||
this.accessFlags = accessFlags
|
||||
this.apiInfo = apiInfo
|
||||
this.defaultValue = defaultValue
|
||||
}
|
||||
}
|
||||
|
||||
-6
@@ -18,7 +18,6 @@
|
||||
package com.itsaky.androidide.lsp.java.indexing.classfile
|
||||
|
||||
import com.itsaky.androidide.lsp.java.indexing.IJavaType
|
||||
import com.itsaky.androidide.lsp.java.indexing.apiinfo.ApiInfo
|
||||
import io.realm.RealmList
|
||||
import io.realm.annotations.Index
|
||||
import io.realm.annotations.PrimaryKey
|
||||
@@ -66,9 +65,6 @@ open class JavaAnnotation : IJavaType<JavaField, AnnotationElement> {
|
||||
@RealmField("accessFlags")
|
||||
override var accessFlags: Int = 0
|
||||
|
||||
@RealmField("apiInfo")
|
||||
override var apiInfo: ApiInfo? = null
|
||||
|
||||
@RealmField("fields")
|
||||
override var fields: RealmList<JavaField>? = null
|
||||
|
||||
@@ -90,7 +86,6 @@ open class JavaAnnotation : IJavaType<JavaField, AnnotationElement> {
|
||||
accessFlags: Int,
|
||||
isInner: Boolean = false,
|
||||
superInterfacesFqn: RealmList<String>? = null,
|
||||
apiInfo: ApiInfo? = null,
|
||||
fields: RealmList<JavaField>? = null,
|
||||
methods: RealmList<AnnotationElement>? = null
|
||||
): JavaAnnotation {
|
||||
@@ -102,7 +97,6 @@ open class JavaAnnotation : IJavaType<JavaField, AnnotationElement> {
|
||||
this.superClassFqn = superClassFqn
|
||||
this.superInterfacesFqn = superInterfacesFqn
|
||||
this.accessFlags = accessFlags
|
||||
this.apiInfo = apiInfo
|
||||
this.fields = fields
|
||||
this.methods = methods
|
||||
}
|
||||
|
||||
@@ -18,7 +18,6 @@
|
||||
package com.itsaky.androidide.lsp.java.indexing.classfile
|
||||
|
||||
import com.itsaky.androidide.lsp.java.indexing.IJavaType
|
||||
import com.itsaky.androidide.lsp.java.indexing.apiinfo.ApiInfo
|
||||
import io.realm.RealmList
|
||||
import io.realm.annotations.Index
|
||||
import io.realm.annotations.PrimaryKey
|
||||
@@ -61,9 +60,6 @@ open class JavaClass : IJavaType<JavaField, JavaMethod> {
|
||||
@RealmField("accessFlags")
|
||||
override var accessFlags: Int = 0
|
||||
|
||||
@RealmField("apiInfo")
|
||||
override var apiInfo: ApiInfo? = null
|
||||
|
||||
@RealmField("fields")
|
||||
override var fields: RealmList<JavaField>? = null
|
||||
|
||||
@@ -83,7 +79,6 @@ open class JavaClass : IJavaType<JavaField, JavaMethod> {
|
||||
isInner: Boolean = false,
|
||||
superClassFqn: String? = null,
|
||||
superInterfacesFqn: RealmList<String>? = null,
|
||||
apiInfo: ApiInfo? = null,
|
||||
fields: RealmList<JavaField>? = null,
|
||||
methods: RealmList<JavaMethod>? = null
|
||||
): JavaClass {
|
||||
@@ -95,7 +90,6 @@ open class JavaClass : IJavaType<JavaField, JavaMethod> {
|
||||
this.superClassFqn = superClassFqn
|
||||
this.superInterfacesFqn = superInterfacesFqn
|
||||
this.accessFlags = accessFlags
|
||||
this.apiInfo = apiInfo
|
||||
this.fields = fields
|
||||
this.methods = methods
|
||||
}
|
||||
|
||||
@@ -18,7 +18,6 @@
|
||||
package com.itsaky.androidide.lsp.java.indexing.classfile
|
||||
|
||||
import com.itsaky.androidide.lsp.java.indexing.IJavaType
|
||||
import com.itsaky.androidide.lsp.java.indexing.apiinfo.ApiInfo
|
||||
import io.realm.RealmList
|
||||
import io.realm.annotations.Index
|
||||
import io.realm.annotations.PrimaryKey
|
||||
@@ -60,9 +59,6 @@ open class JavaEnum : IJavaType<JavaField, JavaMethod> {
|
||||
@RealmField("accessFlags")
|
||||
override var accessFlags: Int = 0
|
||||
|
||||
@RealmField("apiInfo")
|
||||
override var apiInfo: ApiInfo? = null
|
||||
|
||||
@RealmField("fields")
|
||||
override var fields: RealmList<JavaField>? = null
|
||||
|
||||
@@ -82,7 +78,6 @@ open class JavaEnum : IJavaType<JavaField, JavaMethod> {
|
||||
isInner: Boolean = false,
|
||||
superClassFqn: String? = null,
|
||||
superInterfacesFqn: RealmList<String>? = null,
|
||||
apiInfo: ApiInfo? = null,
|
||||
fields: RealmList<JavaField>? = null,
|
||||
methods: RealmList<JavaMethod>? = null
|
||||
): JavaEnum {
|
||||
@@ -94,7 +89,6 @@ open class JavaEnum : IJavaType<JavaField, JavaMethod> {
|
||||
this.superClassFqn = superClassFqn
|
||||
this.superInterfacesFqn = superInterfacesFqn
|
||||
this.accessFlags = accessFlags
|
||||
this.apiInfo = apiInfo
|
||||
this.fields = fields
|
||||
this.methods = methods
|
||||
}
|
||||
|
||||
@@ -18,7 +18,6 @@
|
||||
package com.itsaky.androidide.lsp.java.indexing.classfile
|
||||
|
||||
import com.itsaky.androidide.lsp.java.indexing.IJavaSymbol
|
||||
import com.itsaky.androidide.lsp.java.indexing.apiinfo.ApiInfo
|
||||
import io.realm.RealmResults
|
||||
import io.realm.annotations.Index
|
||||
import io.realm.annotations.LinkingObjects
|
||||
@@ -41,9 +40,6 @@ open class JavaField : IJavaSymbol {
|
||||
@RealmField("accessFlags")
|
||||
override var accessFlags: Int = 0
|
||||
|
||||
@RealmField("apiInfo")
|
||||
override var apiInfo: ApiInfo? = null
|
||||
|
||||
@RealmField("constantValue")
|
||||
var constantValue: JavaConstant? = null
|
||||
|
||||
@@ -56,14 +52,12 @@ open class JavaField : IJavaSymbol {
|
||||
name: String,
|
||||
type: JavaType,
|
||||
accessFlags: Int,
|
||||
apiInfo: ApiInfo? = null,
|
||||
constantValue: JavaConstant? = null
|
||||
): JavaField {
|
||||
return JavaField().apply {
|
||||
this.name = name
|
||||
this.type = type
|
||||
this.accessFlags = accessFlags
|
||||
this.apiInfo = apiInfo
|
||||
this.constantValue = constantValue
|
||||
}
|
||||
}
|
||||
|
||||
-6
@@ -18,7 +18,6 @@
|
||||
package com.itsaky.androidide.lsp.java.indexing.classfile
|
||||
|
||||
import com.itsaky.androidide.lsp.java.indexing.IJavaType
|
||||
import com.itsaky.androidide.lsp.java.indexing.apiinfo.ApiInfo
|
||||
import io.realm.RealmList
|
||||
import io.realm.annotations.Index
|
||||
import io.realm.annotations.PrimaryKey
|
||||
@@ -61,9 +60,6 @@ open class JavaInterface : IJavaType<JavaField, JavaMethod> {
|
||||
@RealmField("accessFlags")
|
||||
override var accessFlags: Int = 0
|
||||
|
||||
@RealmField("apiInfo")
|
||||
override var apiInfo: ApiInfo? = null
|
||||
|
||||
@RealmField("fields")
|
||||
override var fields: RealmList<JavaField>? = null
|
||||
|
||||
@@ -82,7 +78,6 @@ open class JavaInterface : IJavaType<JavaField, JavaMethod> {
|
||||
accessFlags: Int,
|
||||
isInner: Boolean = false,
|
||||
superInterfacesFqn: RealmList<String>? = null,
|
||||
apiInfo: ApiInfo? = null,
|
||||
fields: RealmList<JavaField>? = null,
|
||||
methods: RealmList<JavaMethod>? = null
|
||||
): JavaInterface {
|
||||
@@ -93,7 +88,6 @@ open class JavaInterface : IJavaType<JavaField, JavaMethod> {
|
||||
this.isInner = isInner
|
||||
this.superInterfacesFqn = superInterfacesFqn
|
||||
this.accessFlags = accessFlags
|
||||
this.apiInfo = apiInfo
|
||||
this.fields = fields
|
||||
this.methods = methods
|
||||
}
|
||||
|
||||
@@ -18,7 +18,6 @@
|
||||
package com.itsaky.androidide.lsp.java.indexing.classfile
|
||||
|
||||
import com.itsaky.androidide.lsp.java.indexing.IJavaSymbol
|
||||
import com.itsaky.androidide.lsp.java.indexing.apiinfo.ApiInfo
|
||||
import io.realm.RealmList
|
||||
import io.realm.RealmResults
|
||||
import io.realm.annotations.Index
|
||||
@@ -47,9 +46,6 @@ open class JavaMethod : IJavaSymbol {
|
||||
@RealmField("accessFlags")
|
||||
override var accessFlags: Int = 0
|
||||
|
||||
@RealmField("apiInfo")
|
||||
override var apiInfo: ApiInfo? = null
|
||||
|
||||
@LinkingObjects("methods")
|
||||
val ofClass: RealmResults<JavaClass>? = null
|
||||
|
||||
@@ -75,14 +71,12 @@ open class JavaMethod : IJavaSymbol {
|
||||
paramsTypes: RealmList<JavaType>,
|
||||
returnType: JavaType,
|
||||
accessFlags: Int,
|
||||
apiInfo: ApiInfo? = null,
|
||||
): JavaMethod {
|
||||
return JavaMethod().apply {
|
||||
this.name = name
|
||||
this.paramsTypes = paramsTypes
|
||||
this.returnType = returnType
|
||||
this.accessFlags = accessFlags
|
||||
this.apiInfo = apiInfo
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -18,8 +18,8 @@
|
||||
package com.itsaky.androidide.lsp.java.indexing.classfile
|
||||
|
||||
import com.google.common.base.Objects
|
||||
import com.itsaky.androidide.lsp.java.indexing.ICloneable
|
||||
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
|
||||
@@ -163,7 +163,7 @@ open class JavaType : ISharedJavaIndexable, ICloneable {
|
||||
// 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_CLASS = 11
|
||||
internal const val __KIND_ENUM = 12
|
||||
internal const val __KIND_ANNOTATION = 13
|
||||
internal const val __KIND_ARRAY = 14
|
||||
|
||||
+1
-1
@@ -18,8 +18,8 @@
|
||||
package com.itsaky.androidide.lsp.java.indexing.classfile
|
||||
|
||||
import com.google.common.base.Objects
|
||||
import com.itsaky.androidide.db.utils.isEqualTo
|
||||
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
|
||||
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* 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.progress.IProgressIndicator
|
||||
import com.itsaky.androidide.projects.api.Project
|
||||
import java.io.File
|
||||
|
||||
/**
|
||||
* @author Akash Yadav
|
||||
*/
|
||||
@AutoService(IIndexService::class)
|
||||
internal class JavaIndexServiceImpl : IIndexService {
|
||||
|
||||
override fun scanFiles(project: Project): Collection<File> {
|
||||
return emptySet()
|
||||
}
|
||||
|
||||
override suspend fun indexFiles(
|
||||
project: Project,
|
||||
progress: IProgressIndicator,
|
||||
files: Collection<File>
|
||||
) {
|
||||
}
|
||||
}
|
||||
+2
-1
@@ -127,7 +127,8 @@ include(
|
||||
":core:app",
|
||||
":core:common",
|
||||
":core:database",
|
||||
":core:indexing",
|
||||
":core:indexing-api",
|
||||
":core:indexing-core",
|
||||
":core:lsp-api",
|
||||
":core:lsp-models",
|
||||
":core:projects",
|
||||
|
||||
@@ -68,7 +68,7 @@ dependencies {
|
||||
|
||||
androidTestImplementation(projects.core.common)
|
||||
androidTestImplementation(projects.core.database)
|
||||
androidTestImplementation(projects.core.indexing)
|
||||
androidTestImplementation(projects.core.indexingApi)
|
||||
androidTestImplementation(projects.java.lsp)
|
||||
androidTestImplementation(projects.utilities.shared)
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* 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.progress
|
||||
|
||||
import androidx.annotation.FloatRange
|
||||
|
||||
/**
|
||||
* A progress indicator reports progress of a specific task.
|
||||
*
|
||||
* @author Akash Yadav
|
||||
*/
|
||||
interface IProgressIndicator {
|
||||
|
||||
/**
|
||||
* Called when the task begins its execution.
|
||||
*/
|
||||
fun onStart()
|
||||
|
||||
/**
|
||||
* Called each time the task progresses.
|
||||
*
|
||||
* @param progress The progress of the task. Must be a value between 0 (start) and 1 (finish).
|
||||
*/
|
||||
fun onProgress(@FloatRange(from = 0.0, to = 1.0) progress: Float)
|
||||
|
||||
/**
|
||||
* Called when the task finishes its execution.
|
||||
*/
|
||||
fun onFinish()
|
||||
}
|
||||
Reference in New Issue
Block a user