6
6
7
7
import { Type , isType } from '../graph/type-node.js' ;
8
8
import { TypirServices } from '../typir.js' ;
9
- import { RuleOptions , RuleRegistry } from '../utils/rule-registration.js' ;
9
+ import { RuleCollectorListener , RuleOptions , RuleRegistry } from '../utils/rule-registration.js' ;
10
10
import { TypirProblem , isSpecificTypirProblem } from '../utils/utils-definitions.js' ;
11
11
import { TypeCheckStrategy , createTypeCheckStrategy } from '../utils/utils-type-comparison.js' ;
12
+ import { removeFromArray , toArray } from '../utils/utils.js' ;
12
13
import { TypeInferenceCollector } from './inference.js' ;
13
14
import { ProblemPrinter } from './printing.js' ;
14
15
@@ -180,6 +181,12 @@ export class DefaultValidationConstraints<LanguageType = unknown> implements Val
180
181
}
181
182
}
182
183
184
+
185
+ export interface ValidationCollectorListener < LanguageType = unknown > {
186
+ onAddedValidationRule ( rule : ValidationRule < LanguageType > , options : ValidationRuleOptions ) : void ;
187
+ onRemovedValidationRule ( rule : ValidationRule < LanguageType > , options : ValidationRuleOptions ) : void ;
188
+ }
189
+
183
190
export interface ValidationRuleOptions extends RuleOptions {
184
191
// no additional properties so far
185
192
}
@@ -201,18 +208,26 @@ export interface ValidationCollector<LanguageType = unknown> {
201
208
* @param options the same options as given for the registration of the validation rule must be given for the removal!
202
209
*/
203
210
removeValidationRule ( rule : ValidationRule < LanguageType > , options ?: Partial < ValidationRuleOptions > ) : void ;
211
+
212
+ addListener ( listener : ValidationCollectorListener < LanguageType > ) : void ;
213
+ removeListener ( listener : ValidationCollectorListener < LanguageType > ) : void ;
204
214
}
205
215
206
- export class DefaultValidationCollector < LanguageType = unknown > implements ValidationCollector < LanguageType > {
216
+ export class DefaultValidationCollector < LanguageType = unknown > implements ValidationCollector < LanguageType > , RuleCollectorListener < ValidationRule < LanguageType > > {
207
217
protected readonly services : TypirServices < LanguageType > ;
218
+ protected readonly listeners : Array < ValidationCollectorListener < LanguageType > > = [ ] ;
208
219
209
220
protected readonly ruleRegistryStateLess : RuleRegistry < ValidationRuleStateless < LanguageType > > ;
210
221
protected readonly ruleRegistryBeforeAfter : RuleRegistry < ValidationRuleWithBeforeAfter < LanguageType > > ;
211
222
212
223
constructor ( services : TypirServices < LanguageType > ) {
213
224
this . services = services ;
225
+
214
226
this . ruleRegistryStateLess = new RuleRegistry ( services as TypirServices ) ;
227
+ this . ruleRegistryStateLess . addListener ( this ) ;
228
+
215
229
this . ruleRegistryBeforeAfter = new RuleRegistry ( services as TypirServices ) ;
230
+ this . ruleRegistryBeforeAfter . addListener ( this ) ;
216
231
}
217
232
218
233
protected createAcceptor ( problems : Array < ValidationProblem < LanguageType > > ) : ValidationProblemAcceptor < LanguageType > {
@@ -298,4 +313,77 @@ export class DefaultValidationCollector<LanguageType = unknown> implements Valid
298
313
}
299
314
}
300
315
316
+ addListener ( listener : ValidationCollectorListener < LanguageType > ) : void {
317
+ this . listeners . push ( listener ) ;
318
+ }
319
+ removeListener ( listener : ValidationCollectorListener < LanguageType > ) : void {
320
+ removeFromArray ( listener , this . listeners ) ;
321
+ }
322
+
323
+ onAddedRule ( rule : ValidationRule < LanguageType , LanguageType > , diffOptions : RuleOptions ) : void {
324
+ // listeners of the composite will be notified about all added inner rules
325
+ this . listeners . forEach ( listener => listener . onAddedValidationRule ( rule , diffOptions ) ) ;
326
+ }
327
+ onRemovedRule ( rule : ValidationRule < LanguageType , LanguageType > , diffOptions : RuleOptions ) : void {
328
+ // listeners of the composite will be notified about all removed inner rules
329
+ this . listeners . forEach ( listener => listener . onRemovedValidationRule ( rule , diffOptions ) ) ;
330
+ }
331
+ }
332
+
333
+
334
+ export class CompositeValidationRule < LanguageType = unknown > extends DefaultValidationCollector < LanguageType > implements ValidationRuleWithBeforeAfter < LanguageType > {
335
+ /** The collector for inference rules, at which this composite rule should be registered. */
336
+ protected readonly collectorToRegisterThisRule : ValidationCollector < LanguageType > ;
337
+
338
+ constructor ( services : TypirServices < LanguageType > , collectorToRegisterThisRule : ValidationCollector < LanguageType > ) {
339
+ super ( services ) ;
340
+ this . collectorToRegisterThisRule = collectorToRegisterThisRule ;
341
+ }
342
+
343
+ beforeValidation ( languageRoot : LanguageType , accept : ValidationProblemAcceptor < LanguageType > , _typir : TypirServices < LanguageType > ) : void {
344
+ this . validateBefore ( languageRoot ) . forEach ( v => accept ( v ) ) ;
345
+ }
346
+
347
+ validation ( languageNode : LanguageType , accept : ValidationProblemAcceptor < LanguageType > , _typir : TypirServices < LanguageType > ) : void {
348
+ this . validate ( languageNode ) . forEach ( v => accept ( v ) ) ;
349
+ }
350
+
351
+ afterValidation ( languageRoot : LanguageType , accept : ValidationProblemAcceptor < LanguageType > , _typir : TypirServices < LanguageType > ) : void {
352
+ this . validateAfter ( languageRoot ) . forEach ( v => accept ( v ) ) ;
353
+ }
354
+
355
+ override onAddedRule ( rule : ValidationRule < LanguageType , LanguageType > , diffOptions : RuleOptions ) : void {
356
+ // an inner rule was added
357
+ super . onAddedRule ( rule , diffOptions ) ;
358
+
359
+ // this composite rule needs to be registered also for all the language keys of the new inner rule
360
+ this . collectorToRegisterThisRule . addValidationRule ( this , {
361
+ ...diffOptions ,
362
+ boundToType : undefined ,
363
+ } ) ;
364
+ }
365
+
366
+ override onRemovedRule ( rule : ValidationRule < LanguageType > , diffOptions : RuleOptions ) : void {
367
+ // an inner rule was removed
368
+ super . onRemovedRule ( rule , diffOptions ) ;
369
+
370
+ // remove this composite rule for all language keys for which no inner rules are registered anymore
371
+ if ( diffOptions . languageKey === undefined ) {
372
+ if ( this . ruleRegistryStateLess . getRulesByLanguageKey ( undefined ) . length <= 0 && this . ruleRegistryBeforeAfter . getRulesByLanguageKey ( undefined ) . length <= 0 ) {
373
+ this . collectorToRegisterThisRule . removeValidationRule ( this , {
374
+ ...diffOptions ,
375
+ languageKey : undefined ,
376
+ boundToType : undefined , // a composite rule is never bound to a type, since it manages this feature itself
377
+ } ) ;
378
+ }
379
+ } else {
380
+ const languageKeysToUnregister = toArray ( diffOptions . languageKey )
381
+ . filter ( key => this . ruleRegistryStateLess . getRulesByLanguageKey ( key ) . length <= 0 && this . ruleRegistryBeforeAfter . getRulesByLanguageKey ( key ) . length <= 0 ) ;
382
+ this . collectorToRegisterThisRule . removeValidationRule ( this , {
383
+ ...diffOptions ,
384
+ languageKey : languageKeysToUnregister ,
385
+ boundToType : undefined , // a composite rule is never bound to a type, since it manages this feature itself
386
+ } ) ;
387
+ }
388
+ }
301
389
}
0 commit comments