Skip to content

Commit acc1163

Browse files
committed
fix(indexing): split indexing logic into 'api' and 'core' modules
1 parent 6f85257 commit acc1163

52 files changed

Lines changed: 1037 additions & 409 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.idea/compiler.xml

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

core/app/build.gradle.kts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,8 @@ dependencies {
129129
// Local projects here
130130
implementation(projects.core.actions)
131131
implementation(projects.core.common)
132-
implementation(projects.core.indexing)
132+
implementation(projects.core.indexingApi)
133+
implementation(projects.core.indexingCore)
133134
implementation(projects.core.lspApi)
134135
implementation(projects.core.projects)
135136
implementation(projects.core.resources)

java/lsp/src/main/java/com/itsaky/androidide/lsp/java/indexing/ICloneable.kt renamed to core/common/src/main/java/com/itsaky/androidide/models/ICloneable.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
1616
*/
1717

18-
package com.itsaky.androidide.lsp.java.indexing
18+
package com.itsaky.androidide.models
1919

2020
/**
2121
* Anything that can be cloned.

core/database/src/main/java/com/itsaky/androidide/db/IRealmProvider.kt

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,28 @@ interface IRealmProvider {
5454
*/
5555
const val PATH_SEPARATOR = '/'
5656

57+
/**
58+
* Create the path for the given segments.
59+
*
60+
* @param first The first (root) path segment.
61+
* @param rest The rest of the path segments.
62+
* @return The created path string.
63+
*/
64+
@JvmStatic
65+
fun createPath(first: String, vararg rest: String): String {
66+
67+
val sb = StringBuilder()
68+
sb.append(PATH_SEPARATOR)
69+
sb.append(first)
70+
71+
for (segment in rest) {
72+
sb.append(PATH_SEPARATOR)
73+
sb.append(segment)
74+
}
75+
76+
return sb.toString()
77+
}
78+
5779
/**
5880
* Get the [IRealmProvider] instance, finding the implementation if necessary.
5981
*/
Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ groupConfig {
2929
}
3030

3131
android {
32-
namespace = "${BuildConfig.packageName}.indexing"
32+
namespace = "${BuildConfig.packageName}.indexing.api"
3333
}
3434

3535
dependencies {
@@ -38,6 +38,7 @@ dependencies {
3838

3939
api(projects.core.common)
4040
api(projects.core.database)
41+
api(projects.core.projects)
4142
api(projects.logging.logger)
4243
api(projects.utilities.shared)
4344
}
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
/*
2+
* This file is part of AndroidIDE.
3+
*
4+
* AndroidIDE is free software: you can redistribute it and/or modify
5+
* it under the terms of the GNU General Public License as published by
6+
* the Free Software Foundation, either version 3 of the License, or
7+
* (at your option) any later version.
8+
*
9+
* AndroidIDE is distributed in the hope that it will be useful,
10+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
* GNU General Public License for more details.
13+
*
14+
* You should have received a copy of the GNU General Public License
15+
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
16+
*/
17+
18+
package com.itsaky.androidide.indexing
19+
20+
import io.realm.Realm
21+
22+
/**
23+
* Abstract implementation of [IIndex] for indexing data in the database.
24+
*
25+
* @property params The index parameters.
26+
* @author Akash Yadav
27+
*/
28+
abstract class AbstractDBIndex<T : IIndexable, C : IIndexParams>(
29+
protected val params: IIndexParams?
30+
) : IIndex<T, C> {
31+
32+
protected abstract val realm: Realm
33+
34+
override fun index(symbol: T) {
35+
realm.executeTransaction {
36+
it.insertOrUpdate(symbol)
37+
}
38+
}
39+
40+
override fun indexAsync(symbol: T) {
41+
realm.executeTransactionAsync {
42+
it.insertOrUpdate(symbol)
43+
}
44+
}
45+
46+
override fun indexAll(symbols: Collection<T>) {
47+
realm.executeTransaction {
48+
it.insertOrUpdate(symbols)
49+
}
50+
}
51+
52+
override fun indexAllAsync(symbols: Collection<T>) {
53+
realm.executeTransactionAsync { it.insertOrUpdate(symbols) }
54+
}
55+
56+
override fun delete() {
57+
realm.deleteAll()
58+
}
59+
}

core/indexing/src/main/java/com/itsaky/androidide/indexing/IIndex.kt renamed to core/indexing-api/src/main/java/com/itsaky/androidide/indexing/IIndex.kt

Lines changed: 29 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -18,16 +18,26 @@
1818
package com.itsaky.androidide.indexing
1919

2020
import com.itsaky.androidide.db.IRealmProvider
21-
import com.itsaky.androidide.indexing.internal.DefaultIndex
2221

2322
/**
2423
* An index of symbols of type [IndexableT].
2524
*
2625
* @author Akash Yadav
2726
*/
28-
interface IIndex<IndexableT : IIndexable> {
27+
interface IIndex<IndexableT : IIndexable, ConfigT : IIndexParams> {
2928

3029
companion object {
30+
31+
/**
32+
* The default batch size for indexing.
33+
*/
34+
const val DEFAULT_PUT_BATCH_SIZE = 100
35+
36+
/**
37+
* Base path for indices.
38+
*/
39+
const val INDEX_BASE_PATH = "${IRealmProvider.PATH_SEPARATOR}index"
40+
3141
private const val DEF_IS_ASYNC = true
3242

3343
/**
@@ -53,40 +63,39 @@ interface IIndex<IndexableT : IIndexable> {
5363
*
5464
* @param symbol The symbol to index.
5565
*/
56-
fun index(symbol: IndexableT, async: Boolean = DEF_IS_ASYNC)
66+
fun index(symbol: IndexableT)
67+
68+
/**
69+
* Index the given symbol asynchronously.
70+
*
71+
* @param symbol The symbol to index.
72+
*/
73+
fun indexAsync(symbol: IndexableT)
5774

5875
/**
5976
* Index the given symbols.
6077
*
6178
* @param symbols The symbols to index.
6279
*/
63-
fun indexAll(symbols: Collection<IndexableT>, async: Boolean = DEF_IS_ASYNC) = indexBatched(
64-
symbols,
65-
symbols.size
66-
)
80+
fun indexAll(symbols: Collection<IndexableT>)
6781

6882
/**
69-
* Index the given symbols in batches.
83+
* Index the given symbols asynchronously.
7084
*
7185
* @param symbols The symbols to index.
72-
* @param batchSize The number of symbols to index in each batch.
7386
*/
74-
fun indexBatched(
75-
symbols: Collection<IndexableT>,
76-
batchSize: Int = DefaultIndex.DEFAULT_PUT_BATCH_SIZE,
77-
async: Boolean = DEF_IS_ASYNC,
78-
)
87+
fun indexAllAsync(symbols: Collection<IndexableT>)
7988

8089
/**
81-
* Create a sub-index in this index.
90+
* Create a sub-index in this index. The default implementation will throw an [UnsupportedOperationException].
8291
*
83-
* @param name The name of the sub-index.
8492
* @return The created sub-index.
8593
*/
86-
fun <I : IIndexable> createSubIndex(
87-
name: String,
88-
config: IndexConfig? = null
89-
): IIndex<I>
94+
fun <I : IIndexable, C : IIndexParams> createSubIndex(
95+
params: IIndexParams? = null
96+
): IIndex<I, C> {
97+
throw UnsupportedOperationException()
98+
}
9099

91100
/**
92101
* Delete the index.
Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
/*
2+
* This file is part of AndroidIDE.
3+
*
4+
* AndroidIDE is free software: you can redistribute it and/or modify
5+
* it under the terms of the GNU General Public License as published by
6+
* the Free Software Foundation, either version 3 of the License, or
7+
* (at your option) any later version.
8+
*
9+
* AndroidIDE is distributed in the hope that it will be useful,
10+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
* GNU General Public License for more details.
13+
*
14+
* You should have received a copy of the GNU General Public License
15+
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
16+
*/
17+
18+
package com.itsaky.androidide.indexing
19+
20+
import com.itsaky.androidide.utils.ServiceLoader
21+
22+
/**
23+
* A factory for creating instances of [IIndex].
24+
*
25+
* @param I The indexable type.
26+
* @param P The index creation params.
27+
* @author Akash Yadav
28+
*/
29+
interface IIndexFactory<I : IIndexable, P : IIndexParams> {
30+
31+
/**
32+
* The parameters used to create the index.
33+
*/
34+
var params: P?
35+
36+
/**
37+
* Get the indexable type for the index.
38+
*/
39+
fun indexableType(): Class<out I>
40+
41+
/**
42+
* Get the parameter type for the index.
43+
*/
44+
fun paramType(): Class<out P>
45+
46+
/**
47+
* Create the index.
48+
*
49+
* @return The created [IIndex].
50+
*/
51+
@Throws(NotFoundException::class)
52+
fun create(): IIndex<I, P>
53+
54+
companion object {
55+
56+
private val factoryCache =
57+
mutableMapOf<Class<out IIndexable>, IIndexFactory<out IIndexable, out IIndexParams>>()
58+
59+
/**
60+
* Finds the index factory for the given symbol type returning `null` if not found.
61+
*
62+
* @param symTyp The symbol type.
63+
* @param paramTyp The parameter type.
64+
* @return The index factory.
65+
*/
66+
fun <T : IIndexable, P : IIndexParams> findFactoryForSymType(
67+
symTyp: Class<out T>,
68+
paramTyp: Class<out P>? = null
69+
) = __findFactoryForSymType(symTyp = symTyp, paramTyp = paramTyp, fail = false)
70+
71+
/**
72+
* Get the index factory for the given symbol type failing if not found.
73+
*
74+
* @param symTyp The symbol type.
75+
* @param paramTyp The parameter type.
76+
* @return The index factory.
77+
*/
78+
fun <T : IIndexable, P : IIndexParams> getFactoryForSymType(
79+
symTyp: Class<out T>,
80+
paramTyp: Class<out P>? = null
81+
) = __findFactoryForSymType(symTyp = symTyp, paramTyp = paramTyp, fail = true)!!
82+
83+
/**
84+
* Finds an index factory for the given symbol type.
85+
*
86+
* @param symTyp The symbol type.
87+
* @param paramTyp The parameter type.
88+
* @param fail Whether to fail if no factory is found.
89+
* @return The index factory.
90+
*/
91+
@Suppress("UNCHECKED_CAST", "FunctionName")
92+
private fun <T : IIndexable, P : IIndexParams> __findFactoryForSymType(
93+
symTyp: Class<out T>,
94+
paramTyp: Class<out P>?,
95+
fail: Boolean = true
96+
): IIndexFactory<T, P>? {
97+
var factory: IIndexFactory<T, P>? = this.factoryCache[symTyp] as IIndexFactory<T, P>?
98+
if (factory != null) {
99+
return factory
100+
}
101+
102+
val impls = ServiceLoader.load(symTyp).iterator()
103+
while (impls.hasNext()) {
104+
val impl = impls.next()
105+
if (impl is IIndexFactory<*, *> && symTyp == impl.indexableType()) {
106+
if (factory == null) {
107+
factory = impl as IIndexFactory<T, P>
108+
continue
109+
}
110+
111+
if (!fail) {
112+
return null
113+
}
114+
115+
throw MultipleFactoriesException(symTyp)
116+
}
117+
}
118+
119+
if (factory != null && paramTyp != null && factory.paramType() != paramTyp) {
120+
if (!fail) {
121+
return null
122+
}
123+
throw InvalidParamTypeException(factory, paramTyp)
124+
}
125+
126+
if (factory == null) {
127+
if (!fail) {
128+
return null
129+
}
130+
131+
throw NotFoundException(symTyp)
132+
}
133+
134+
val typ = factory.indexableType()
135+
136+
factoryCache.put(typ, factory)?.also {
137+
throw IllegalStateException("Invalid cache state")
138+
}
139+
140+
return factory
141+
}
142+
}
143+
144+
/**
145+
* Exception thrown when an [IIndexFactory] cannot be found.
146+
*/
147+
class NotFoundException(sym: Class<out IIndexable>) :
148+
RuntimeException("No index factory found for ${sym.name}")
149+
150+
/**
151+
* Exception thrown when multiple [IIndexFactory]s are found for a single symbol type.
152+
*/
153+
class MultipleFactoriesException(sym: Class<out IIndexable>) :
154+
RuntimeException("Multiple index factories found for ${sym.name}")
155+
156+
/**
157+
* Exception thrown when the index parameters of a factory are of invalid type.
158+
*/
159+
class InvalidParamTypeException(factory: IIndexFactory<*, *>, paramTyp: Class<out IIndexParams>) :
160+
RuntimeException("Factory ${factory.javaClass} expected parameters of type ${factory.paramType()} but an instance of $paramTyp was provided")
161+
}

0 commit comments

Comments
 (0)