Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Proper loading of 3rd party rulesets and reporters. #577

Merged
merged 1 commit into from
Sep 16, 2019
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
100 changes: 48 additions & 52 deletions ktlint/src/main/kotlin/com/pinterest/ktlint/Main.kt
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ import com.pinterest.ktlint.internal.printHelpOrVersionUsage
import java.io.File
import java.io.IOException
import java.io.PrintStream
import java.net.URL
import java.net.URLClassLoader
import java.net.URLDecoder
import java.nio.file.Paths
Expand Down Expand Up @@ -223,9 +222,6 @@ class KtlintCommandLine {
}
val start = System.currentTimeMillis()

// load 3rd party ruleset(s) (if any)
if (rulesets.isNotEmpty()) loadJARs(rulesets)

// Detect custom rulesets that have not been moved to the new package
if (ServiceLoader.load(com.github.shyiko.ktlint.core.RuleSetProvider::class.java).any()) {
System.err.println("[ERROR] Cannot load custom ruleset!")
Expand All @@ -234,17 +230,9 @@ class KtlintCommandLine {
exitProcess(1)
}

// standard should go first
val ruleSetProviders =
ServiceLoader.load(RuleSetProvider::class.java)
.map { it.get().id to it }
.filter { (id) -> experimental || id != "experimental" }
.sortedBy { if (it.first == "standard") "\u0000${it.first}" else it.first }
if (debug) {
ruleSetProviders.forEach { System.err.println("[DEBUG] Discovered ruleset \"${it.first}\"") }
}
val ruleSetProviders = loadRulesets(rulesets)
val tripped = AtomicBoolean()
val reporter = loadReporter() { tripped.get() }
val reporter = loadReporter { tripped.get() }
data class LintErrorWithCorrectionInfo(val err: LintError, val corrected: Boolean)
val userData = listOfNotNull(
"android" to android.toString(),
Expand All @@ -262,7 +250,7 @@ class KtlintCommandLine {
formatFile(
fileName,
fileContent,
ruleSetProviders.map { it.second.get() },
ruleSetProviders.map { it.value.get() },
userData,
editorConfigPath,
debug
Expand All @@ -289,7 +277,7 @@ class KtlintCommandLine {
lintFile(
fileName,
fileContent,
ruleSetProviders.map { it.second.get() },
ruleSetProviders.map { it.value.get() },
userData,
editorConfigPath,
debug
Expand Down Expand Up @@ -363,20 +351,7 @@ class KtlintCommandLine {
)
}
.distinct()
val reporterLoader = ServiceLoader.load(ReporterProvider::class.java)
val reporterProviderById = reporterLoader.associateBy { it.id }
val missingReporters = tpls
.filter { !reporterProviderById.containsKey(it.id) }
.mapNotNull { it.artifact }
.distinct()
if (missingReporters.isNotEmpty()) {
loadJARs(missingReporters)
reporterLoader.reload()
reporterLoader.associateBy { it.id }
}
if (debug) {
reporterProviderById.forEach { (id) -> System.err.println("[DEBUG] Discovered reporter \"$id\"") }
}
val reporterProviderById = loadReporters(tpls.mapNotNull { it.artifact })
fun ReporterTemplate.toReporter(): Reporter {
val reporterProvider = reporterProviderById[id]
if (reporterProvider == null) {
Expand Down Expand Up @@ -478,22 +453,6 @@ class KtlintCommandLine {

private fun <T> List<T>.head(limit: Int) = if (limit == size) this else this.subList(0, limit)

// fixme: isn't going to work on JDK 9
private fun loadJARs(artifacts: List<String>) {
val jarUrls = artifacts
.map {
val artifactFile = File(expandTilde(it))
if (!artifactFile.exists()) {
System.err.println("Error: $it does not exist")
exitProcess(1)
}
artifactFile.toURI().toURL()
}

val classLoader = ClassLoader.getSystemClassLoader() as URLClassLoader
classLoader.addURLs(jarUrls)
}

private fun parseQuery(query: String) =
query.split("&")
.fold(LinkedHashMap<String, String>()) { map, s ->
Expand All @@ -508,12 +467,6 @@ class KtlintCommandLine {
map
}

private fun URLClassLoader.addURLs(url: Iterable<URL>) {
val method = URLClassLoader::class.java.getDeclaredMethod("addURL", URL::class.java)
method.isAccessible = true
url.forEach { method.invoke(this, it) }
}

private fun File.mkdirsOrFail() {
if (!mkdirs() && !isDirectory) {
throw IOException("Unable to create \"${this}\" directory")
Expand Down Expand Up @@ -578,4 +531,47 @@ class KtlintCommandLine {
producer.join()
}
}

private fun loadRulesets(externalRulesetsJarPaths: List<String>) = ServiceLoader
.load(
RuleSetProvider::class.java,
URLClassLoader(externalRulesetsJarPaths.toFilesURIList().toTypedArray())
)
.associateBy {
val key = it.get().id
// standard should go first
if (key == "standard") "\u0000$key" else key
}
.filterKeys { experimental || it != "experimental" }
.toSortedMap()
.also {
if (debug) {
it.forEach { entry ->
println("[DEBUG] Discovered ruleset with \"${entry.key}\" id.")
}
}
}

private fun loadReporters(externalReportersJarPaths: List<String>) = ServiceLoader
.load(
ReporterProvider::class.java,
URLClassLoader(externalReportersJarPaths.toFilesURIList().toTypedArray())
)
.associateBy { it.id }
.also {
if (debug) {
it.forEach { entry ->
println("[DEBUG] Discovered reporter with \"${entry.key}\" id.")
}
}
}

private fun List<String>.toFilesURIList() = map {
val jarFile = File(expandTilde(it))
if (!jarFile.exists()) {
println("Error: $it does not exist")
exitProcess(1)
}
jarFile.toURI().toURL()
}
}