From acc11639244ef036b9f7e4388e170adc4d047c28 Mon Sep 17 00:00:00 2001 From: Akash Yadav Date: Thu, 13 Jun 2024 17:39:44 +0530 Subject: [PATCH] fix(indexing): split indexing logic into 'api' and 'core' modules --- .idea/compiler.xml | 1 + core/app/build.gradle.kts | 3 +- .../itsaky/androidide/models}/ICloneable.kt | 2 +- .../itsaky/androidide/db/IRealmProvider.kt | 22 +++ core/{indexing => indexing-api}/.gitignore | 0 .../build.gradle.kts | 3 +- .../androidide/indexing/AbstractDBIndex.kt | 59 +++++++ .../com/itsaky/androidide/indexing/IIndex.kt | 49 +++--- .../androidide/indexing/IIndexFactory.kt | 161 ++++++++++++++++++ .../androidide/indexing/IIndexParams.kt | 25 +++ .../androidide/indexing/IIndexService.kt | 48 ++++++ .../itsaky/androidide/indexing/IIndexable.kt | 0 .../androidide/indexing/IIndexerParams.kt} | 25 +-- core/indexing-core/.gitignore | 1 + core/indexing-core/build.gradle.kts | 47 +++++ .../platform/ApiVersionsIndexFactory.kt | 44 +++++ .../internal/platform/ApiVersionsIndexImpl.kt | 51 ++++++ .../internal/platform/ApiVersionsParser.kt | 88 +++++----- .../internal/platform/PlatformIndexService.kt | 47 +++++ .../core/platform/AbstractIndexFactory.kt | 32 ++++ .../indexing/core/platform/ApiVersion.kt | 32 ++-- .../core/platform/ClassOrMemberInfo.kt | 49 ++++++ .../core/platform/IApiVersionsIndex.kt | 34 ++++ .../indexing/core/platform/IPlatformIndex.kt | 38 +++++ .../core/platform/IPlatformIndexable.kt | 27 +++ .../core/platform/ApiVersionParserTest.kt | 33 ++-- .../platform/CollectingApiVersionsParser.kt | 79 +++++++++ .../androidide/indexing/IndexProvider.kt | 71 -------- .../indexing/internal/DefaultIndex.kt | 76 --------- .../androidide/projects/ProjectManagerImpl.kt | 9 +- .../androidide/projects/api/AndroidModule.kt | 26 ++- java/lsp/build.gradle.kts | 9 +- .../lsp/java/indexing/JavaClassCRUDTest.kt | 1 - .../lsp/java/indexing/JavaIndexerTest.kt | 1 - .../lsp/java/indexing/SharedModelCRUDTest.kt | 7 +- .../lsp/java/indexing/IJavaSymbol.kt | 3 - .../lsp/java/indexing/JavaIndexer.kt | 56 ------ .../JavaIndexingRealmModule.kt | 18 +- .../lsp/java/indexing/JavaJarModelBuilder.kt | 23 +-- .../indexing/classfile/AnnotationElement.kt | 8 +- .../java/indexing/classfile/JavaAnnotation.kt | 6 - .../lsp/java/indexing/classfile/JavaClass.kt | 6 - .../lsp/java/indexing/classfile/JavaEnum.kt | 6 - .../lsp/java/indexing/classfile/JavaField.kt | 6 - .../java/indexing/classfile/JavaInterface.kt | 6 - .../lsp/java/indexing/classfile/JavaMethod.kt | 6 - .../lsp/java/indexing/classfile/JavaType.kt | 6 +- .../classfile/annotationElementValues.kt | 2 +- .../indexing/internal/JavaIndexServiceImpl.kt | 42 +++++ settings.gradle.kts | 3 +- testing/benchmarks/build.gradle.kts | 4 +- .../androidide/progress/IProgressIndicator.kt | 45 +++++ 52 files changed, 1037 insertions(+), 409 deletions(-) rename {java/lsp/src/main/java/com/itsaky/androidide/lsp/java/indexing => core/common/src/main/java/com/itsaky/androidide/models}/ICloneable.kt (94%) rename core/{indexing => indexing-api}/.gitignore (100%) rename core/{indexing => indexing-api}/build.gradle.kts (93%) create mode 100644 core/indexing-api/src/main/java/com/itsaky/androidide/indexing/AbstractDBIndex.kt rename core/{indexing => indexing-api}/src/main/java/com/itsaky/androidide/indexing/IIndex.kt (65%) create mode 100644 core/indexing-api/src/main/java/com/itsaky/androidide/indexing/IIndexFactory.kt create mode 100644 core/indexing-api/src/main/java/com/itsaky/androidide/indexing/IIndexParams.kt create mode 100644 core/indexing-api/src/main/java/com/itsaky/androidide/indexing/IIndexService.kt rename core/{indexing => indexing-api}/src/main/java/com/itsaky/androidide/indexing/IIndexable.kt (100%) rename core/{indexing/src/main/java/com/itsaky/androidide/indexing/IIndexer.kt => indexing-api/src/main/java/com/itsaky/androidide/indexing/IIndexerParams.kt} (52%) create mode 100644 core/indexing-core/.gitignore create mode 100644 core/indexing-core/build.gradle.kts create mode 100644 core/indexing-core/src/main/java/com/itsaky/androidide/indexing/core/internal/platform/ApiVersionsIndexFactory.kt create mode 100644 core/indexing-core/src/main/java/com/itsaky/androidide/indexing/core/internal/platform/ApiVersionsIndexImpl.kt rename java/lsp/src/main/java/com/itsaky/androidide/lsp/java/indexing/apiinfo/ApiInfoParser.kt => core/indexing-core/src/main/java/com/itsaky/androidide/indexing/core/internal/platform/ApiVersionsParser.kt (68%) create mode 100644 core/indexing-core/src/main/java/com/itsaky/androidide/indexing/core/internal/platform/PlatformIndexService.kt create mode 100644 core/indexing-core/src/main/java/com/itsaky/androidide/indexing/core/platform/AbstractIndexFactory.kt rename java/lsp/src/main/java/com/itsaky/androidide/lsp/java/indexing/apiinfo/ApiInfo.kt => core/indexing-core/src/main/java/com/itsaky/androidide/indexing/core/platform/ApiVersion.kt (78%) create mode 100644 core/indexing-core/src/main/java/com/itsaky/androidide/indexing/core/platform/ClassOrMemberInfo.kt create mode 100644 core/indexing-core/src/main/java/com/itsaky/androidide/indexing/core/platform/IApiVersionsIndex.kt create mode 100644 core/indexing-core/src/main/java/com/itsaky/androidide/indexing/core/platform/IPlatformIndex.kt create mode 100644 core/indexing-core/src/main/java/com/itsaky/androidide/indexing/core/platform/IPlatformIndexable.kt rename java/lsp/src/test/java/com/itsaky/androidide/lsp/java/indexing/apiinfo/ApiInfoParserTest.kt => core/indexing-core/src/test/java/com/itsaky/androidide/indexing/core/platform/ApiVersionParserTest.kt (93%) create mode 100644 core/indexing-core/src/test/java/com/itsaky/androidide/indexing/core/platform/CollectingApiVersionsParser.kt delete mode 100644 core/indexing/src/main/java/com/itsaky/androidide/indexing/IndexProvider.kt delete mode 100644 core/indexing/src/main/java/com/itsaky/androidide/indexing/internal/DefaultIndex.kt delete mode 100644 java/lsp/src/main/java/com/itsaky/androidide/lsp/java/indexing/JavaIndexer.kt rename java/lsp/src/main/java/com/itsaky/androidide/lsp/java/indexing/{classfile => }/JavaIndexingRealmModule.kt (54%) create mode 100644 java/lsp/src/main/java/com/itsaky/androidide/lsp/java/indexing/internal/JavaIndexServiceImpl.kt create mode 100644 utilities/shared/src/main/java/com/itsaky/androidide/progress/IProgressIndicator.kt diff --git a/.idea/compiler.xml b/.idea/compiler.xml index b38ea905d..ea12ba260 100644 --- a/.idea/compiler.xml +++ b/.idea/compiler.xml @@ -116,6 +116,7 @@ + diff --git a/core/app/build.gradle.kts b/core/app/build.gradle.kts index f21cdc6ce..ff00e1018 100755 --- a/core/app/build.gradle.kts +++ b/core/app/build.gradle.kts @@ -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) diff --git a/java/lsp/src/main/java/com/itsaky/androidide/lsp/java/indexing/ICloneable.kt b/core/common/src/main/java/com/itsaky/androidide/models/ICloneable.kt similarity index 94% rename from java/lsp/src/main/java/com/itsaky/androidide/lsp/java/indexing/ICloneable.kt rename to core/common/src/main/java/com/itsaky/androidide/models/ICloneable.kt index d3ad0e584..c7dd4108e 100644 --- a/java/lsp/src/main/java/com/itsaky/androidide/lsp/java/indexing/ICloneable.kt +++ b/core/common/src/main/java/com/itsaky/androidide/models/ICloneable.kt @@ -15,7 +15,7 @@ * along with AndroidIDE. If not, see . */ -package com.itsaky.androidide.lsp.java.indexing +package com.itsaky.androidide.models /** * Anything that can be cloned. diff --git a/core/database/src/main/java/com/itsaky/androidide/db/IRealmProvider.kt b/core/database/src/main/java/com/itsaky/androidide/db/IRealmProvider.kt index 539d91208..2bf6ec341 100644 --- a/core/database/src/main/java/com/itsaky/androidide/db/IRealmProvider.kt +++ b/core/database/src/main/java/com/itsaky/androidide/db/IRealmProvider.kt @@ -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. */ diff --git a/core/indexing/.gitignore b/core/indexing-api/.gitignore similarity index 100% rename from core/indexing/.gitignore rename to core/indexing-api/.gitignore diff --git a/core/indexing/build.gradle.kts b/core/indexing-api/build.gradle.kts similarity index 93% rename from core/indexing/build.gradle.kts rename to core/indexing-api/build.gradle.kts index 705f6d522..1c0dd6055 100644 --- a/core/indexing/build.gradle.kts +++ b/core/indexing-api/build.gradle.kts @@ -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) } \ No newline at end of file diff --git a/core/indexing-api/src/main/java/com/itsaky/androidide/indexing/AbstractDBIndex.kt b/core/indexing-api/src/main/java/com/itsaky/androidide/indexing/AbstractDBIndex.kt new file mode 100644 index 000000000..bb1718e80 --- /dev/null +++ b/core/indexing-api/src/main/java/com/itsaky/androidide/indexing/AbstractDBIndex.kt @@ -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 . + */ + +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( + protected val params: IIndexParams? +) : IIndex { + + 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) { + realm.executeTransaction { + it.insertOrUpdate(symbols) + } + } + + override fun indexAllAsync(symbols: Collection) { + realm.executeTransactionAsync { it.insertOrUpdate(symbols) } + } + + override fun delete() { + realm.deleteAll() + } +} \ No newline at end of file diff --git a/core/indexing/src/main/java/com/itsaky/androidide/indexing/IIndex.kt b/core/indexing-api/src/main/java/com/itsaky/androidide/indexing/IIndex.kt similarity index 65% rename from core/indexing/src/main/java/com/itsaky/androidide/indexing/IIndex.kt rename to core/indexing-api/src/main/java/com/itsaky/androidide/indexing/IIndex.kt index a31568765..f7aa0822f 100644 --- a/core/indexing/src/main/java/com/itsaky/androidide/indexing/IIndex.kt +++ b/core/indexing-api/src/main/java/com/itsaky/androidide/indexing/IIndex.kt @@ -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 { +interface IIndex { 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 { * * @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, async: Boolean = DEF_IS_ASYNC) = indexBatched( - symbols, - symbols.size - ) + fun indexAll(symbols: Collection) /** - * 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, - batchSize: Int = DefaultIndex.DEFAULT_PUT_BATCH_SIZE, - async: Boolean = DEF_IS_ASYNC, - ) + fun indexAllAsync(symbols: Collection) /** - * 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 createSubIndex( - name: String, - config: IndexConfig? = null - ): IIndex + fun createSubIndex( + params: IIndexParams? = null + ): IIndex { + throw UnsupportedOperationException() + } /** * Delete the index. diff --git a/core/indexing-api/src/main/java/com/itsaky/androidide/indexing/IIndexFactory.kt b/core/indexing-api/src/main/java/com/itsaky/androidide/indexing/IIndexFactory.kt new file mode 100644 index 000000000..5d90387e0 --- /dev/null +++ b/core/indexing-api/src/main/java/com/itsaky/androidide/indexing/IIndexFactory.kt @@ -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 . + */ + +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 { + + /** + * The parameters used to create the index. + */ + var params: P? + + /** + * Get the indexable type for the index. + */ + fun indexableType(): Class + + /** + * Get the parameter type for the index. + */ + fun paramType(): Class + + /** + * Create the index. + * + * @return The created [IIndex]. + */ + @Throws(NotFoundException::class) + fun create(): IIndex + + companion object { + + private val factoryCache = + mutableMapOf, IIndexFactory>() + + /** + * 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 findFactoryForSymType( + symTyp: Class, + paramTyp: Class? = 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 getFactoryForSymType( + symTyp: Class, + paramTyp: Class? = 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 __findFactoryForSymType( + symTyp: Class, + paramTyp: Class?, + fail: Boolean = true + ): IIndexFactory? { + var factory: IIndexFactory? = this.factoryCache[symTyp] as IIndexFactory? + 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 + 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) : + 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) : + 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) : + RuntimeException("Factory ${factory.javaClass} expected parameters of type ${factory.paramType()} but an instance of $paramTyp was provided") +} \ No newline at end of file diff --git a/core/indexing-api/src/main/java/com/itsaky/androidide/indexing/IIndexParams.kt b/core/indexing-api/src/main/java/com/itsaky/androidide/indexing/IIndexParams.kt new file mode 100644 index 000000000..72334e44b --- /dev/null +++ b/core/indexing-api/src/main/java/com/itsaky/androidide/indexing/IIndexParams.kt @@ -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 . + */ + +package com.itsaky.androidide.indexing + +/** + * Index configuration + * + * @author Akash Yadav + */ +interface IIndexParams \ No newline at end of file diff --git a/core/indexing-api/src/main/java/com/itsaky/androidide/indexing/IIndexService.kt b/core/indexing-api/src/main/java/com/itsaky/androidide/indexing/IIndexService.kt new file mode 100644 index 000000000..87c44237e --- /dev/null +++ b/core/indexing-api/src/main/java/com/itsaky/androidide/indexing/IIndexService.kt @@ -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 . + */ + +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 + + /** + * 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) +} \ No newline at end of file diff --git a/core/indexing/src/main/java/com/itsaky/androidide/indexing/IIndexable.kt b/core/indexing-api/src/main/java/com/itsaky/androidide/indexing/IIndexable.kt similarity index 100% rename from core/indexing/src/main/java/com/itsaky/androidide/indexing/IIndexable.kt rename to core/indexing-api/src/main/java/com/itsaky/androidide/indexing/IIndexable.kt diff --git a/core/indexing/src/main/java/com/itsaky/androidide/indexing/IIndexer.kt b/core/indexing-api/src/main/java/com/itsaky/androidide/indexing/IIndexerParams.kt similarity index 52% rename from core/indexing/src/main/java/com/itsaky/androidide/indexing/IIndexer.kt rename to core/indexing-api/src/main/java/com/itsaky/androidide/indexing/IIndexerParams.kt index 150af9906..c71548d09 100644 --- a/core/indexing/src/main/java/com/itsaky/androidide/indexing/IIndexer.kt +++ b/core/indexing-api/src/main/java/com/itsaky/androidide/indexing/IIndexerParams.kt @@ -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 : AutoCloseable { +interface IIndexerParams { /** - * 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, params: Params): Collection> - - /** - * The parameters for the indexer. - */ - interface Params + val indexParams: ParamsT } \ No newline at end of file diff --git a/core/indexing-core/.gitignore b/core/indexing-core/.gitignore new file mode 100644 index 000000000..42afabfd2 --- /dev/null +++ b/core/indexing-core/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/core/indexing-core/build.gradle.kts b/core/indexing-core/build.gradle.kts new file mode 100644 index 000000000..ef00bab3e --- /dev/null +++ b/core/indexing-core/build.gradle.kts @@ -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 . + */ + +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) +} \ No newline at end of file diff --git a/core/indexing-core/src/main/java/com/itsaky/androidide/indexing/core/internal/platform/ApiVersionsIndexFactory.kt b/core/indexing-core/src/main/java/com/itsaky/androidide/indexing/core/internal/platform/ApiVersionsIndexFactory.kt new file mode 100644 index 000000000..ae5d928da --- /dev/null +++ b/core/indexing-core/src/main/java/com/itsaky/androidide/indexing/core/internal/platform/ApiVersionsIndexFactory.kt @@ -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 . + */ + +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() { + + override fun indexableType(): Class { + return ClassOrMemberInfo::class.java + } + + override fun paramType(): Class { + return Params::class.java + } + + override fun create(): IIndex { + return ApiVersionsIndexImpl.newInstance(params) + } +} \ No newline at end of file diff --git a/core/indexing-core/src/main/java/com/itsaky/androidide/indexing/core/internal/platform/ApiVersionsIndexImpl.kt b/core/indexing-core/src/main/java/com/itsaky/androidide/indexing/core/internal/platform/ApiVersionsIndexImpl.kt new file mode 100644 index 000000000..2e320881a --- /dev/null +++ b/core/indexing-core/src/main/java/com/itsaky/androidide/indexing/core/internal/platform/ApiVersionsIndexImpl.kt @@ -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 . + */ + +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(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) + } +} \ No newline at end of file diff --git a/java/lsp/src/main/java/com/itsaky/androidide/lsp/java/indexing/apiinfo/ApiInfoParser.kt b/core/indexing-core/src/main/java/com/itsaky/androidide/indexing/core/internal/platform/ApiVersionsParser.kt similarity index 68% rename from java/lsp/src/main/java/com/itsaky/androidide/lsp/java/indexing/apiinfo/ApiInfoParser.kt rename to core/indexing-core/src/main/java/com/itsaky/androidide/indexing/core/internal/platform/ApiVersionsParser.kt index cdcad65c7..fce8d95c8 100644 --- a/java/lsp/src/main/java/com/itsaky/androidide/lsp/java/indexing/apiinfo/ApiInfoParser.kt +++ b/core/indexing-core/src/main/java/com/itsaky/androidide/indexing/core/internal/platform/ApiVersionsParser.kt @@ -15,9 +15,10 @@ * along with AndroidIDE. If not, see . */ -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() - private val apiInfo = HashMap>>() + // 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() 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 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) } } \ No newline at end of file diff --git a/core/indexing-core/src/main/java/com/itsaky/androidide/indexing/core/internal/platform/PlatformIndexService.kt b/core/indexing-core/src/main/java/com/itsaky/androidide/indexing/core/internal/platform/PlatformIndexService.kt new file mode 100644 index 000000000..af3fdb3f4 --- /dev/null +++ b/core/indexing-core/src/main/java/com/itsaky/androidide/indexing/core/internal/platform/PlatformIndexService.kt @@ -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 . + */ + +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 { + return mutableListOf().apply { + project.findAndroidModules().forEach { androidModule -> + add(androidModule.getPlatformDir() ?: return@forEach) + } + } + } + + override suspend fun indexFiles( + project: Project, + progress: IProgressIndicator, + files: Collection + ) { + TODO("Not yet implemented") + } +} \ No newline at end of file diff --git a/core/indexing-core/src/main/java/com/itsaky/androidide/indexing/core/platform/AbstractIndexFactory.kt b/core/indexing-core/src/main/java/com/itsaky/androidide/indexing/core/platform/AbstractIndexFactory.kt new file mode 100644 index 000000000..9675deab7 --- /dev/null +++ b/core/indexing-core/src/main/java/com/itsaky/androidide/indexing/core/platform/AbstractIndexFactory.kt @@ -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 . + */ + +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 : IIndexFactory { + + override var params: P? = null +} \ No newline at end of file diff --git a/java/lsp/src/main/java/com/itsaky/androidide/lsp/java/indexing/apiinfo/ApiInfo.kt b/core/indexing-core/src/main/java/com/itsaky/androidide/indexing/core/platform/ApiVersion.kt similarity index 78% rename from java/lsp/src/main/java/com/itsaky/androidide/lsp/java/indexing/apiinfo/ApiInfo.kt rename to core/indexing-core/src/main/java/com/itsaky/androidide/indexing/core/platform/ApiVersion.kt index 04b7e968d..0ccbe0714 100644 --- a/java/lsp/src/main/java/com/itsaky/androidide/lsp/java/indexing/apiinfo/ApiInfo.kt +++ b/core/indexing-core/src/main/java/com/itsaky/androidide/indexing/core/platform/ApiVersion.kt @@ -15,11 +15,11 @@ * along with AndroidIDE. If not, see . */ -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 diff --git a/core/indexing-core/src/main/java/com/itsaky/androidide/indexing/core/platform/ClassOrMemberInfo.kt b/core/indexing-core/src/main/java/com/itsaky/androidide/indexing/core/platform/ClassOrMemberInfo.kt new file mode 100644 index 000000000..cbf01f1b9 --- /dev/null +++ b/core/indexing-core/src/main/java/com/itsaky/androidide/indexing/core/platform/ClassOrMemberInfo.kt @@ -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 . + */ + +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 +} \ No newline at end of file diff --git a/core/indexing-core/src/main/java/com/itsaky/androidide/indexing/core/platform/IApiVersionsIndex.kt b/core/indexing-core/src/main/java/com/itsaky/androidide/indexing/core/platform/IApiVersionsIndex.kt new file mode 100644 index 000000000..344126c44 --- /dev/null +++ b/core/indexing-core/src/main/java/com/itsaky/androidide/indexing/core/platform/IApiVersionsIndex.kt @@ -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 . + */ + +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 { + + class Params( + val platformId: String + ) : IIndexParams +} \ No newline at end of file diff --git a/core/indexing-core/src/main/java/com/itsaky/androidide/indexing/core/platform/IPlatformIndex.kt b/core/indexing-core/src/main/java/com/itsaky/androidide/indexing/core/platform/IPlatformIndex.kt new file mode 100644 index 000000000..d4aa51e1b --- /dev/null +++ b/core/indexing-core/src/main/java/com/itsaky/androidide/indexing/core/platform/IPlatformIndex.kt @@ -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 . + */ + +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 : IIndex { + + companion object { + + /** + * Base path for the platform index. + */ + val PLATFORM_INDEX_BASE_PATH = IRealmProvider.createPath(IIndex.INDEX_BASE_PATH, "platform") + } +} \ No newline at end of file diff --git a/core/indexing-core/src/main/java/com/itsaky/androidide/indexing/core/platform/IPlatformIndexable.kt b/core/indexing-core/src/main/java/com/itsaky/androidide/indexing/core/platform/IPlatformIndexable.kt new file mode 100644 index 000000000..17f4ffc0a --- /dev/null +++ b/core/indexing-core/src/main/java/com/itsaky/androidide/indexing/core/platform/IPlatformIndexable.kt @@ -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 . + */ + +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 \ No newline at end of file diff --git a/java/lsp/src/test/java/com/itsaky/androidide/lsp/java/indexing/apiinfo/ApiInfoParserTest.kt b/core/indexing-core/src/test/java/com/itsaky/androidide/indexing/core/platform/ApiVersionParserTest.kt similarity index 93% rename from java/lsp/src/test/java/com/itsaky/androidide/lsp/java/indexing/apiinfo/ApiInfoParserTest.kt rename to core/indexing-core/src/test/java/com/itsaky/androidide/indexing/core/platform/ApiVersionParserTest.kt index 85b176b29..8f2d8a507 100644 --- a/java/lsp/src/test/java/com/itsaky/androidide/lsp/java/indexing/apiinfo/ApiInfoParserTest.kt +++ b/core/indexing-core/src/test/java/com/itsaky/androidide/indexing/core/platform/ApiVersionParserTest.kt @@ -15,7 +15,7 @@ * along with AndroidIDE. If not, see . */ -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("") val inputStream = ByteArrayInputStream(xmlBuilder.toString().toByteArray()) - val parser = ApiInfoParser() + val parser = CollectingApiVersionsParser() parser.parse(inputStream) for (i in 1..1000) { diff --git a/core/indexing-core/src/test/java/com/itsaky/androidide/indexing/core/platform/CollectingApiVersionsParser.kt b/core/indexing-core/src/test/java/com/itsaky/androidide/indexing/core/platform/CollectingApiVersionsParser.kt new file mode 100644 index 000000000..f34548c8f --- /dev/null +++ b/core/indexing-core/src/test/java/com/itsaky/androidide/indexing/core/platform/CollectingApiVersionsParser.kt @@ -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 . + */ + +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>>() + + 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) + } + } + } +} \ No newline at end of file diff --git a/core/indexing/src/main/java/com/itsaky/androidide/indexing/IndexProvider.kt b/core/indexing/src/main/java/com/itsaky/androidide/indexing/IndexProvider.kt deleted file mode 100644 index 5d8fcfc3c..000000000 --- a/core/indexing/src/main/java/com/itsaky/androidide/indexing/IndexProvider.kt +++ /dev/null @@ -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 . - */ - -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 getRootIndex( - name: String, - config: IndexConfig? = null - ): IIndex { - 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 getIndexForPath( - path: String, - config: IndexConfig? = null - ): IIndex { - 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(name, path, realm) - return index - } -} - -/** - * The [IndexConfig] is used to configure the index. - */ -typealias IndexConfig = RealmConfiguration.Builder.() -> Unit diff --git a/core/indexing/src/main/java/com/itsaky/androidide/indexing/internal/DefaultIndex.kt b/core/indexing/src/main/java/com/itsaky/androidide/indexing/internal/DefaultIndex.kt deleted file mode 100644 index eef4333c0..000000000 --- a/core/indexing/src/main/java/com/itsaky/androidide/indexing/internal/DefaultIndex.kt +++ /dev/null @@ -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 . - */ - -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 private constructor( - override val name: String, - override val path: String, - private val realm: Realm, -) : IIndex { - - companion object { - private val log = LoggerFactory.getLogger(DefaultIndex::class.java) - const val DEFAULT_PUT_BATCH_SIZE = 100 - - /** - * Creates a new [IIndex] instance. - */ - fun create( - name: String, - path: String, - realm: Realm, - ): IIndex { - return DefaultIndex(name, path, realm) - } - } - -// override fun query(condition: QueryCondition): Query { -// return box.query(condition).build() -// } - - override fun index(symbol: IndexableT, async: Boolean) { - TODO("Not yet implemented") - } - - override fun indexBatched(symbols: Collection, batchSize: Int, async: Boolean) { - TODO("Not yet implemented") - } - - override fun createSubIndex( - name: String, - config: IndexConfig? - ): IIndex { - return IndexProvider.getIndexForPath(IIndex.createIndexPath(this.path, name), config) - } - - override fun delete() { - TODO("Not yet implemented") - } -} \ No newline at end of file diff --git a/core/projects/src/main/java/com/itsaky/androidide/projects/ProjectManagerImpl.kt b/core/projects/src/main/java/com/itsaky/androidide/projects/ProjectManagerImpl.kt index ea03c16fa..db48a1312 100644 --- a/core/projects/src/main/java/com/itsaky/androidide/projects/ProjectManagerImpl.kt +++ b/core/projects/src/main/java/com/itsaky/androidide/projects/ProjectManagerImpl.kt @@ -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() } diff --git a/core/projects/src/main/java/com/itsaky/androidide/projects/api/AndroidModule.kt b/core/projects/src/main/java/com/itsaky/androidide/projects/api/AndroidModule.kt index ca0c29d4b..3494bd813 100644 --- a/core/projects/src/main/java/com/itsaky/androidide/projects/api/AndroidModule.kt +++ b/core/projects/src/main/java/com/itsaky/androidide/projects/api/AndroidModule.kt @@ -163,8 +163,10 @@ open class AndroidModule( // Class must be open because BaseXMLTest mocks this.. override fun getSourceDirectories(): Set { 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 } diff --git a/java/lsp/build.gradle.kts b/java/lsp/build.gradle.kts index 0d0a0d418..d68b77d75 100644 --- a/java/lsp/build.gradle.kts +++ b/java/lsp/build.gradle.kts @@ -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) @@ -80,4 +83,4 @@ dependencies { androidTestImplementation(projects.testing.androidTest) androidTestImplementation(projects.utilities.shared) -} \ No newline at end of file +} diff --git a/java/lsp/src/androidTest/java/com/itsaky/androidide/lsp/java/indexing/JavaClassCRUDTest.kt b/java/lsp/src/androidTest/java/com/itsaky/androidide/lsp/java/indexing/JavaClassCRUDTest.kt index e039e7a79..458ee380b 100644 --- a/java/lsp/src/androidTest/java/com/itsaky/androidide/lsp/java/indexing/JavaClassCRUDTest.kt +++ b/java/lsp/src/androidTest/java/com/itsaky/androidide/lsp/java/indexing/JavaClassCRUDTest.kt @@ -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 diff --git a/java/lsp/src/androidTest/java/com/itsaky/androidide/lsp/java/indexing/JavaIndexerTest.kt b/java/lsp/src/androidTest/java/com/itsaky/androidide/lsp/java/indexing/JavaIndexerTest.kt index 32c1a3333..e746f7d59 100644 --- a/java/lsp/src/androidTest/java/com/itsaky/androidide/lsp/java/indexing/JavaIndexerTest.kt +++ b/java/lsp/src/androidTest/java/com/itsaky/androidide/lsp/java/indexing/JavaIndexerTest.kt @@ -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 diff --git a/java/lsp/src/androidTest/java/com/itsaky/androidide/lsp/java/indexing/SharedModelCRUDTest.kt b/java/lsp/src/androidTest/java/com/itsaky/androidide/lsp/java/indexing/SharedModelCRUDTest.kt index c87955129..30d8c59e8 100644 --- a/java/lsp/src/androidTest/java/com/itsaky/androidide/lsp/java/indexing/SharedModelCRUDTest.kt +++ b/java/lsp/src/androidTest/java/com/itsaky/androidide/lsp/java/indexing/SharedModelCRUDTest.kt @@ -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) } diff --git a/java/lsp/src/main/java/com/itsaky/androidide/lsp/java/indexing/IJavaSymbol.kt b/java/lsp/src/main/java/com/itsaky/androidide/lsp/java/indexing/IJavaSymbol.kt index 457665f37..07ebb9f2e 100644 --- a/java/lsp/src/main/java/com/itsaky/androidide/lsp/java/indexing/IJavaSymbol.kt +++ b/java/lsp/src/main/java/com/itsaky/androidide/lsp/java/indexing/IJavaSymbol.kt @@ -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? } \ No newline at end of file diff --git a/java/lsp/src/main/java/com/itsaky/androidide/lsp/java/indexing/JavaIndexer.kt b/java/lsp/src/main/java/com/itsaky/androidide/lsp/java/indexing/JavaIndexer.kt deleted file mode 100644 index 8e1d8d3e3..000000000 --- a/java/lsp/src/main/java/com/itsaky/androidide/lsp/java/indexing/JavaIndexer.kt +++ /dev/null @@ -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 . - */ - -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 -) : IIndexer { - - companion object { - const val INDEX_PATH = "/java" - } - - private val coroutineScope = CoroutineScope(Dispatchers.IO) - override val indexPath: String = INDEX_PATH - - override fun index(index: IIndex, params: Params): Collection> { - return jarPaths.map { jar -> - coroutineScope.async { - val worker = JavaJarModelBuilder(jar) -// worker.index(index) - } - } - } - - override fun close() { - coroutineScope.cancelIfActive("Cancellation requested") - } -} \ No newline at end of file diff --git a/java/lsp/src/main/java/com/itsaky/androidide/lsp/java/indexing/classfile/JavaIndexingRealmModule.kt b/java/lsp/src/main/java/com/itsaky/androidide/lsp/java/indexing/JavaIndexingRealmModule.kt similarity index 54% rename from java/lsp/src/main/java/com/itsaky/androidide/lsp/java/indexing/classfile/JavaIndexingRealmModule.kt rename to java/lsp/src/main/java/com/itsaky/androidide/lsp/java/indexing/JavaIndexingRealmModule.kt index dffd0a4c7..14f5b8931 100644 --- a/java/lsp/src/main/java/com/itsaky/androidide/lsp/java/indexing/classfile/JavaIndexingRealmModule.kt +++ b/java/lsp/src/main/java/com/itsaky/androidide/lsp/java/indexing/JavaIndexingRealmModule.kt @@ -15,9 +15,22 @@ * along with AndroidIDE. If not, see . */ -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, diff --git a/java/lsp/src/main/java/com/itsaky/androidide/lsp/java/indexing/JavaJarModelBuilder.kt b/java/lsp/src/main/java/com/itsaky/androidide/lsp/java/indexing/JavaJarModelBuilder.kt index f0c5459d4..a70038026 100644 --- a/java/lsp/src/main/java/com/itsaky/androidide/lsp/java/indexing/JavaJarModelBuilder.kt +++ b/java/lsp/src/main/java/com/itsaky/androidide/lsp/java/indexing/JavaJarModelBuilder.kt @@ -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() + } + }) } } } diff --git a/java/lsp/src/main/java/com/itsaky/androidide/lsp/java/indexing/classfile/AnnotationElement.kt b/java/lsp/src/main/java/com/itsaky/androidide/lsp/java/indexing/classfile/AnnotationElement.kt index 5c542769c..f0fe17bdc 100644 --- a/java/lsp/src/main/java/com/itsaky/androidide/lsp/java/indexing/classfile/AnnotationElement.kt +++ b/java/lsp/src/main/java/com/itsaky/androidide/lsp/java/indexing/classfile/AnnotationElement.kt @@ -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? = 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 } } diff --git a/java/lsp/src/main/java/com/itsaky/androidide/lsp/java/indexing/classfile/JavaAnnotation.kt b/java/lsp/src/main/java/com/itsaky/androidide/lsp/java/indexing/classfile/JavaAnnotation.kt index a997d04af..4a3a7d4a2 100644 --- a/java/lsp/src/main/java/com/itsaky/androidide/lsp/java/indexing/classfile/JavaAnnotation.kt +++ b/java/lsp/src/main/java/com/itsaky/androidide/lsp/java/indexing/classfile/JavaAnnotation.kt @@ -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 { @RealmField("accessFlags") override var accessFlags: Int = 0 - @RealmField("apiInfo") - override var apiInfo: ApiInfo? = null - @RealmField("fields") override var fields: RealmList? = null @@ -90,7 +86,6 @@ open class JavaAnnotation : IJavaType { accessFlags: Int, isInner: Boolean = false, superInterfacesFqn: RealmList? = null, - apiInfo: ApiInfo? = null, fields: RealmList? = null, methods: RealmList? = null ): JavaAnnotation { @@ -102,7 +97,6 @@ open class JavaAnnotation : IJavaType { this.superClassFqn = superClassFqn this.superInterfacesFqn = superInterfacesFqn this.accessFlags = accessFlags - this.apiInfo = apiInfo this.fields = fields this.methods = methods } diff --git a/java/lsp/src/main/java/com/itsaky/androidide/lsp/java/indexing/classfile/JavaClass.kt b/java/lsp/src/main/java/com/itsaky/androidide/lsp/java/indexing/classfile/JavaClass.kt index 80e1e138d..8fcde79eb 100644 --- a/java/lsp/src/main/java/com/itsaky/androidide/lsp/java/indexing/classfile/JavaClass.kt +++ b/java/lsp/src/main/java/com/itsaky/androidide/lsp/java/indexing/classfile/JavaClass.kt @@ -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 { @RealmField("accessFlags") override var accessFlags: Int = 0 - @RealmField("apiInfo") - override var apiInfo: ApiInfo? = null - @RealmField("fields") override var fields: RealmList? = null @@ -83,7 +79,6 @@ open class JavaClass : IJavaType { isInner: Boolean = false, superClassFqn: String? = null, superInterfacesFqn: RealmList? = null, - apiInfo: ApiInfo? = null, fields: RealmList? = null, methods: RealmList? = null ): JavaClass { @@ -95,7 +90,6 @@ open class JavaClass : IJavaType { this.superClassFqn = superClassFqn this.superInterfacesFqn = superInterfacesFqn this.accessFlags = accessFlags - this.apiInfo = apiInfo this.fields = fields this.methods = methods } diff --git a/java/lsp/src/main/java/com/itsaky/androidide/lsp/java/indexing/classfile/JavaEnum.kt b/java/lsp/src/main/java/com/itsaky/androidide/lsp/java/indexing/classfile/JavaEnum.kt index 0d654f392..78d9ca644 100644 --- a/java/lsp/src/main/java/com/itsaky/androidide/lsp/java/indexing/classfile/JavaEnum.kt +++ b/java/lsp/src/main/java/com/itsaky/androidide/lsp/java/indexing/classfile/JavaEnum.kt @@ -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 { @RealmField("accessFlags") override var accessFlags: Int = 0 - @RealmField("apiInfo") - override var apiInfo: ApiInfo? = null - @RealmField("fields") override var fields: RealmList? = null @@ -82,7 +78,6 @@ open class JavaEnum : IJavaType { isInner: Boolean = false, superClassFqn: String? = null, superInterfacesFqn: RealmList? = null, - apiInfo: ApiInfo? = null, fields: RealmList? = null, methods: RealmList? = null ): JavaEnum { @@ -94,7 +89,6 @@ open class JavaEnum : IJavaType { this.superClassFqn = superClassFqn this.superInterfacesFqn = superInterfacesFqn this.accessFlags = accessFlags - this.apiInfo = apiInfo this.fields = fields this.methods = methods } diff --git a/java/lsp/src/main/java/com/itsaky/androidide/lsp/java/indexing/classfile/JavaField.kt b/java/lsp/src/main/java/com/itsaky/androidide/lsp/java/indexing/classfile/JavaField.kt index 9bce4e299..b1b809ffa 100644 --- a/java/lsp/src/main/java/com/itsaky/androidide/lsp/java/indexing/classfile/JavaField.kt +++ b/java/lsp/src/main/java/com/itsaky/androidide/lsp/java/indexing/classfile/JavaField.kt @@ -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 } } diff --git a/java/lsp/src/main/java/com/itsaky/androidide/lsp/java/indexing/classfile/JavaInterface.kt b/java/lsp/src/main/java/com/itsaky/androidide/lsp/java/indexing/classfile/JavaInterface.kt index c7185778c..130e055cf 100644 --- a/java/lsp/src/main/java/com/itsaky/androidide/lsp/java/indexing/classfile/JavaInterface.kt +++ b/java/lsp/src/main/java/com/itsaky/androidide/lsp/java/indexing/classfile/JavaInterface.kt @@ -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 { @RealmField("accessFlags") override var accessFlags: Int = 0 - @RealmField("apiInfo") - override var apiInfo: ApiInfo? = null - @RealmField("fields") override var fields: RealmList? = null @@ -82,7 +78,6 @@ open class JavaInterface : IJavaType { accessFlags: Int, isInner: Boolean = false, superInterfacesFqn: RealmList? = null, - apiInfo: ApiInfo? = null, fields: RealmList? = null, methods: RealmList? = null ): JavaInterface { @@ -93,7 +88,6 @@ open class JavaInterface : IJavaType { this.isInner = isInner this.superInterfacesFqn = superInterfacesFqn this.accessFlags = accessFlags - this.apiInfo = apiInfo this.fields = fields this.methods = methods } diff --git a/java/lsp/src/main/java/com/itsaky/androidide/lsp/java/indexing/classfile/JavaMethod.kt b/java/lsp/src/main/java/com/itsaky/androidide/lsp/java/indexing/classfile/JavaMethod.kt index c5542ba6c..69635b067 100644 --- a/java/lsp/src/main/java/com/itsaky/androidide/lsp/java/indexing/classfile/JavaMethod.kt +++ b/java/lsp/src/main/java/com/itsaky/androidide/lsp/java/indexing/classfile/JavaMethod.kt @@ -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? = null @@ -75,14 +71,12 @@ open class JavaMethod : IJavaSymbol { paramsTypes: RealmList, 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 } } } diff --git a/java/lsp/src/main/java/com/itsaky/androidide/lsp/java/indexing/classfile/JavaType.kt b/java/lsp/src/main/java/com/itsaky/androidide/lsp/java/indexing/classfile/JavaType.kt index bb1290bf4..206529d25 100644 --- a/java/lsp/src/main/java/com/itsaky/androidide/lsp/java/indexing/classfile/JavaType.kt +++ b/java/lsp/src/main/java/com/itsaky/androidide/lsp/java/indexing/classfile/JavaType.kt @@ -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 @@ -146,7 +146,7 @@ open class JavaType : ISharedJavaIndexable, ICloneable { override fun toString(): String { return "JavaType(id=$id, name=$name, kind=$kind, arrayDims=$arrayDims)" } - + companion object { const val KIND_UNKNOWN = -1 const val KIND_BOOLEAN = 0 @@ -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 diff --git a/java/lsp/src/main/java/com/itsaky/androidide/lsp/java/indexing/classfile/annotationElementValues.kt b/java/lsp/src/main/java/com/itsaky/androidide/lsp/java/indexing/classfile/annotationElementValues.kt index ac30e1706..743352a7a 100644 --- a/java/lsp/src/main/java/com/itsaky/androidide/lsp/java/indexing/classfile/annotationElementValues.kt +++ b/java/lsp/src/main/java/com/itsaky/androidide/lsp/java/indexing/classfile/annotationElementValues.kt @@ -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 diff --git a/java/lsp/src/main/java/com/itsaky/androidide/lsp/java/indexing/internal/JavaIndexServiceImpl.kt b/java/lsp/src/main/java/com/itsaky/androidide/lsp/java/indexing/internal/JavaIndexServiceImpl.kt new file mode 100644 index 000000000..533ef1db3 --- /dev/null +++ b/java/lsp/src/main/java/com/itsaky/androidide/lsp/java/indexing/internal/JavaIndexServiceImpl.kt @@ -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 . + */ + +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 { + return emptySet() + } + + override suspend fun indexFiles( + project: Project, + progress: IProgressIndicator, + files: Collection + ) { + } +} \ No newline at end of file diff --git a/settings.gradle.kts b/settings.gradle.kts index d1a8b7f86..cb0bcab4d 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -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", diff --git a/testing/benchmarks/build.gradle.kts b/testing/benchmarks/build.gradle.kts index ccf535e39..1a30a2b45 100644 --- a/testing/benchmarks/build.gradle.kts +++ b/testing/benchmarks/build.gradle.kts @@ -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) -} \ No newline at end of file +} diff --git a/utilities/shared/src/main/java/com/itsaky/androidide/progress/IProgressIndicator.kt b/utilities/shared/src/main/java/com/itsaky/androidide/progress/IProgressIndicator.kt new file mode 100644 index 000000000..4f078da13 --- /dev/null +++ b/utilities/shared/src/main/java/com/itsaky/androidide/progress/IProgressIndicator.kt @@ -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 . + */ + +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() +} \ No newline at end of file