2222package com .github ._1c_syntax .bsl .languageserver .diagnostics .infrastructure ;
2323
2424import com .github ._1c_syntax .bsl .languageserver .configuration .LanguageServerConfiguration ;
25- import com .github ._1c_syntax .bsl .languageserver .configuration .events .LanguageServerConfigurationChangedEvent ;
2625import com .github ._1c_syntax .bsl .languageserver .diagnostics .BSLDiagnostic ;
2726import com .github ._1c_syntax .bsl .languageserver .diagnostics .metadata .DiagnosticInfo ;
2827import com .github ._1c_syntax .bsl .languageserver .utils .Resources ;
29- import com .networknt .schema .JsonSchema ;
30- import com .networknt .schema .JsonSchemaFactory ;
31- import com .networknt .schema .SpecVersion ;
32- import com .networknt .schema .ValidationMessage ;
3328import lombok .RequiredArgsConstructor ;
3429import lombok .extern .slf4j .Slf4j ;
3530import org .eclipse .lsp4j .jsonrpc .messages .Either ;
3631import org .springframework .beans .factory .config .BeanPostProcessor ;
37- import org .springframework .cache .annotation .CacheConfig ;
38- import org .springframework .cache .annotation .CacheEvict ;
39- import org .springframework .cache .annotation .Cacheable ;
4032import org .springframework .context .annotation .Lazy ;
41- import org .springframework .context .event .EventListener ;
42- import org .springframework .core .io .ClassPathResource ;
43- import org .springframework .lang .Nullable ;
4433import org .springframework .stereotype .Component ;
4534
46- import java .io .IOException ;
4735import java .util .Map ;
48- import java .util .Set ;
49- import java .util .concurrent .ConcurrentHashMap ;
5036
5137@ RequiredArgsConstructor
5238@ Component
5339@ Slf4j
54- @ CacheConfig (cacheNames = "diagnosticSchemaValidation" )
5540public class DiagnosticBeanPostProcessor implements BeanPostProcessor {
5641
5742 private final LanguageServerConfiguration configuration ;
5843 private final Map <Class <? extends BSLDiagnostic >, DiagnosticInfo > diagnosticInfos ;
5944 @ Lazy
6045 private final Resources resources ;
61-
62- @ Nullable
63- private JsonSchema parametersSchema ;
64- private final Map <String , JsonSchema > diagnosticSchemas = new ConcurrentHashMap <>();
65-
66- /**
67- * Обработчик события {@link LanguageServerConfigurationChangedEvent}.
68- * <p>
69- * Сбрасывает кеш валидации схем при изменении конфигурации.
70- *
71- * @param event Событие
72- */
73- @ EventListener
74- @ CacheEvict (allEntries = true )
75- public void handleLanguageServerConfigurationChange (LanguageServerConfigurationChangedEvent event ) {
76- // No-op. Служит для сброса кеша при изменении конфигурации
77- }
46+ private final DiagnosticParameterValidator parameterValidator ;
7847
7948 @ Override
8049 public Object postProcessBeforeInitialization (Object bean , String beanName ) {
@@ -106,7 +75,7 @@ public Object postProcessAfterInitialization(Object bean, String beanName) {
10675 try {
10776 // Validate configuration against JSON schema if available
10877 var diagnosticCode = diagnostic .getInfo ().getCode ().getStringValue ();
109- validateDiagnosticConfiguration (diagnosticCode , diagnosticConfiguration .getRight ());
78+ parameterValidator . validateDiagnosticConfiguration (diagnosticCode , diagnosticConfiguration .getRight ());
11079
11180 diagnostic .configure (diagnosticConfiguration .getRight ());
11281 } catch (Exception e ) {
@@ -119,122 +88,4 @@ public Object postProcessAfterInitialization(Object bean, String beanName) {
11988 return diagnostic ;
12089 }
12190
122- /**
123- * Cached validation of diagnostic configuration against JSON schema.
124- * Results are cached per diagnostic class and configuration to improve performance for prototype beans.
125- *
126- * @param diagnosticCode Diagnostic code
127- * @param configuration Configuration map to validate
128- */
129- @ Cacheable
130- public void validateDiagnosticConfiguration (String diagnosticCode , Map <String , Object > configuration ) {
131- try {
132- var schema = getDiagnosticSchema (diagnosticCode );
133- if (schema != null ) {
134- // Use a standalone ObjectMapper to avoid Spring bean dependencies
135- var standaloneMapper = new com .fasterxml .jackson .databind .ObjectMapper ();
136- var configNode = standaloneMapper .valueToTree (configuration );
137- Set <ValidationMessage > errors = schema .validate (configNode );
138-
139- if (!errors .isEmpty ()) {
140- var errorMessages = errors .stream ()
141- .map (ValidationMessage ::getMessage )
142- .reduce ((msg1 , msg2 ) -> msg1 + "; " + msg2 )
143- .orElse ("Unknown validation error" );
144-
145- var localizedMessage = resources .getResourceString (getClass (), "diagnosticSchemaValidationError" ,
146- diagnosticCode , errorMessages );
147- LOGGER .warn (localizedMessage );
148- }
149- }
150- } catch (Exception e ) {
151- // Schema validation failed, but don't prevent diagnostic configuration
152- LOGGER .debug ("Schema validation failed for diagnostic '{}': {}" , diagnosticCode , e .getMessage ());
153- }
154- }
155-
156- private String mapToJsonString (Map <String , Object > map ) {
157- // Simple JSON serialization to avoid ObjectMapper dependency during bean post processing
158- var json = new StringBuilder ();
159- json .append ("{" );
160- var first = true ;
161- for (var entry : map .entrySet ()) {
162- if (!first ) {
163- json .append ("," );
164- }
165- json .append ("\" " ).append (entry .getKey ()).append ("\" :" );
166-
167- var value = entry .getValue ();
168- if (value instanceof String ) {
169- json .append ("\" " ).append (value ).append ("\" " );
170- } else if (value instanceof Boolean || value instanceof Number ) {
171- json .append (value );
172- } else if (value instanceof java .util .Collection ) {
173- json .append ("[" );
174- var items = (java .util .Collection <?>) value ;
175- var firstItem = true ;
176- for (var item : items ) {
177- if (!firstItem ) {
178- json .append ("," );
179- }
180- if (item instanceof String ) {
181- json .append ("\" " ).append (item ).append ("\" " );
182- } else {
183- json .append (item );
184- }
185- firstItem = false ;
186- }
187- json .append ("]" );
188- } else {
189- json .append ("\" " ).append (value ).append ("\" " );
190- }
191- first = false ;
192- }
193- json .append ("}" );
194- return json .toString ();
195- }
196-
197- private JsonSchema getDiagnosticSchema (String diagnosticCode ) {
198- return diagnosticSchemas .computeIfAbsent (diagnosticCode , this ::loadDiagnosticSchema );
199- }
200-
201- private JsonSchema loadDiagnosticSchema (String diagnosticCode ) {
202- try {
203- var schema = getParametersSchema ();
204- if (schema != null ) {
205- // Extract the specific diagnostic schema from the main schema
206- var schemaNode = schema .getSchemaNode ();
207- var definitionsNode = schemaNode .get ("definitions" );
208- if (definitionsNode != null && definitionsNode .has (diagnosticCode )) {
209- var diagnosticSchemaNode = definitionsNode .get (diagnosticCode );
210- var factory = JsonSchemaFactory .getInstance (SpecVersion .VersionFlag .V7 );
211- return factory .getSchema (diagnosticSchemaNode );
212- }
213- }
214- } catch (Exception e ) {
215- LOGGER .debug ("Failed to load schema for diagnostic '{}': {}" , diagnosticCode , e .getMessage ());
216- }
217- return null ;
218- }
219-
220- private JsonSchema getParametersSchema () {
221- if (parametersSchema == null ) {
222- parametersSchema = loadParametersSchema ();
223- }
224- return parametersSchema ;
225- }
226-
227- private JsonSchema loadParametersSchema () {
228- try {
229- var schemaResource = new ClassPathResource ("com/github/_1c_syntax/bsl/languageserver/configuration/parameters-schema.json" );
230- if (schemaResource .exists ()) {
231- var factory = JsonSchemaFactory .getInstance (SpecVersion .VersionFlag .V7 );
232- return factory .getSchema (schemaResource .getInputStream ());
233- }
234- } catch (IOException e ) {
235- LOGGER .warn ("Failed to load parameters schema: {}" , e .getMessage ());
236- }
237- return null ;
238- }
239-
24091}
0 commit comments