@@ -26,6 +26,40 @@ class ConfigInversionLinter : Plugin<Project> {
2626 }
2727}
2828
29+ // Data class for fields from generated class
30+ private data class LoadedConfigFields (
31+ val supported : Set <String >,
32+ val aliasMapping : Map <String , String > = emptyMap()
33+ )
34+
35+ // Cache for fields from generated class
36+ private var cachedConfigFields: LoadedConfigFields ? = null
37+
38+ // Helper function to load fields from the generated class
39+ private fun loadConfigFields (
40+ mainSourceSetOutput : org.gradle.api.file.FileCollection ,
41+ generatedClassName : String
42+ ): LoadedConfigFields {
43+ return cachedConfigFields ? : run {
44+ val urls = mainSourceSetOutput.files.map { it.toURI().toURL() }.toTypedArray()
45+ URLClassLoader (urls, LoadedConfigFields ::class .java.classLoader).use { cl ->
46+ val clazz = Class .forName(generatedClassName, true , cl)
47+
48+ val supportedField = clazz.getField(" SUPPORTED" ).get(null )
49+ @Suppress(" UNCHECKED_CAST" )
50+ val supportedSet = when (supportedField) {
51+ is Set <* > -> supportedField as Set <String >
52+ is Map <* , * > -> supportedField.keys as Set <String >
53+ else -> throw IllegalStateException (" SUPPORTED field must be either Set<String> or Map<String, Any>, but was ${supportedField?.javaClass} " )
54+ }
55+
56+ @Suppress(" UNCHECKED_CAST" )
57+ val aliasMappingMap = clazz.getField(" ALIAS_MAPPING" ).get(null ) as Map <String , String >
58+ LoadedConfigFields (supportedSet, aliasMappingMap)
59+ }.also { cachedConfigFields = it }
60+ }
61+ }
62+
2963/* * Registers `logEnvVarUsages` (scan for DD_/OTEL_ tokens and fail if unsupported). */
3064private fun registerLogEnvVarUsages (target : Project , extension : SupportedTracerConfigurations ) {
3165 val ownerPath = extension.configOwnerPath
@@ -52,16 +86,11 @@ private fun registerLogEnvVarUsages(target: Project, extension: SupportedTracerC
5286 inputs.files(javaFiles)
5387 outputs.upToDateWhen { true }
5488 doLast {
55- // 1) Build classloader from the owner project’s runtime classpath
56- val urls = mainSourceSetOutput.get().get().files.map { it.toURI().toURL() }.toTypedArray()
57- val supported: Set <String > = URLClassLoader (urls, javaClass.classLoader).use { cl ->
58- // 2) Load the generated class + read static field
59- val clazz = Class .forName(generatedFile.get(), true , cl)
60- @Suppress(" UNCHECKED_CAST" )
61- clazz.getField(" SUPPORTED" ).get(null ) as Set <String >
62- }
89+ // 1) Load configuration fields from the generated class
90+ val configFields = loadConfigFields(mainSourceSetOutput.get().get(), generatedFile.get())
91+ val supported = configFields.supported
6392
64- // 3 ) Scan our sources and compare
93+ // 2 ) Scan our sources and compare
6594 val repoRoot = target.projectDir.toPath()
6695 val tokenRegex = Regex (" \" (?:DD_|OTEL_)[A-Za-z0-9_]+\" " )
6796
@@ -79,7 +108,7 @@ private fun registerLogEnvVarUsages(target: Project, extension: SupportedTracerC
79108 }
80109 tokenRegex.findAll(raw).forEach { m ->
81110 val token = m.value.trim(' "' )
82- if (token !in supported) add(" $rel :${i + 1 } -> Unsupported token'$token '" )
111+ if (token !in supported) add(" $rel :${i + 1 } -> Unsupported token '$token '" )
83112 }
84113 }
85114 }
@@ -167,15 +196,9 @@ private fun registerCheckConfigStringsTask(project: Project, extension: Supporte
167196 throw GradleException (" Config directory not found: ${configDir.absolutePath} " )
168197 }
169198
170- val urls = mainSourceSetOutput.get().get().files.map { it.toURI().toURL() }.toTypedArray()
171- val (supported, aliasMapping) = URLClassLoader (urls, javaClass.classLoader).use { cl ->
172- val clazz = Class .forName(generatedFile.get(), true , cl)
173- @Suppress(" UNCHECKED_CAST" )
174- val supportedSet = clazz.getField(" SUPPORTED" ).get(null ) as Set <String >
175- @Suppress(" UNCHECKED_CAST" )
176- val aliasMappingMap = clazz.getField(" ALIAS_MAPPING" ).get(null ) as Map <String , String >
177- Pair (supportedSet, aliasMappingMap)
178- }
199+ val configFields = loadConfigFields(mainSourceSetOutput.get().get(), generatedFile.get())
200+ val supported = configFields.supported
201+ val aliasMapping = configFields.aliasMapping
179202
180203 var parserConfig = ParserConfiguration ()
181204 parserConfig.setLanguageLevel(ParserConfiguration .LanguageLevel .JAVA_8 )
@@ -192,23 +215,23 @@ private fun registerCheckConfigStringsTask(project: Project, extension: Supporte
192215 .map { it as ? FieldDeclaration }
193216 .ifPresent { field ->
194217 if (field.hasModifiers(Modifier .Keyword .PUBLIC , Modifier .Keyword .STATIC , Modifier .Keyword .FINAL ) &&
195- varDecl.typeAsString == " String" ) {
218+ varDecl.typeAsString == " String" ) {
196219
197- val fieldName = varDecl.nameAsString
198- if (fieldName.endsWith(" _DEFAULT" )) return @ifPresent
199- val init = varDecl.initializer.orElse(null ) ? : return @ifPresent
220+ val fieldName = varDecl.nameAsString
221+ if (fieldName.endsWith(" _DEFAULT" )) return @ifPresent
222+ val init = varDecl.initializer.orElse(null ) ? : return @ifPresent
200223
201- if (init !is StringLiteralExpr ) return @ifPresent
202- val rawValue = init .value
224+ if (init !is StringLiteralExpr ) return @ifPresent
225+ val rawValue = init .value
203226
204- val normalized = normalize(rawValue)
205- if (normalized !in supported && normalized !in aliasMapping) {
206- val line = varDecl.range.map { it.begin.line }.orElse(1 )
207- add(" $fileName :$line -> Config '$rawValue ' normalizes to '$normalized ' " +
208- " which is missing from '${extension.jsonFile.get()} '" )
227+ val normalized = normalize(rawValue)
228+ if (normalized !in supported && normalized !in aliasMapping) {
229+ val line = varDecl.range.map { it.begin.line }.orElse(1 )
230+ add(" $fileName :$line -> Config '$rawValue ' normalizes to '$normalized ' " +
231+ " which is missing from '${extension.jsonFile.get()} '" )
232+ }
209233 }
210234 }
211- }
212235 }
213236 }
214237 }
0 commit comments