xref: /aosp_15_r20/external/json-schema-validator/doc/collector-context.md (revision 78c4dd6aa35290980cdcd1623a7e337e8d021c7c)
1*78c4dd6aSAndroid Build Coastguard Worker### CollectorContext
2*78c4dd6aSAndroid Build Coastguard Worker
3*78c4dd6aSAndroid Build Coastguard WorkerThere could be use cases where we want collect the information while we are validating the data. A simple example could be fetching some value from a database or from a microservice based on the data (which could be a text or a JSON object. It should be noted that this should be a simple operation or validation might take more time to complete.) in a given JSON node and the schema keyword we are using.
4*78c4dd6aSAndroid Build Coastguard Worker
5*78c4dd6aSAndroid Build Coastguard WorkerThe fetched data can be stored somewhere so that it can be used later after the validation is done. Since the current validation logic already parses the data and schema, both validation and collecting the required information can be done in one go.
6*78c4dd6aSAndroid Build Coastguard Worker
7*78c4dd6aSAndroid Build Coastguard WorkerThe `CollectorContext` and `Collector` classes are designed to work with this use case.
8*78c4dd6aSAndroid Build Coastguard Worker
9*78c4dd6aSAndroid Build Coastguard Worker#### How to use CollectorContext
10*78c4dd6aSAndroid Build Coastguard Worker
11*78c4dd6aSAndroid Build Coastguard WorkerThe `CollectorContext` is stored as a variable on the `ExecutionContext` that is used during the validation. This allows users to add objects to context at many points in the framework like Formats and Validators where the `ExecutionContext` is available as a parameter.
12*78c4dd6aSAndroid Build Coastguard Worker
13*78c4dd6aSAndroid Build Coastguard WorkerCollectors are added to `CollectorContext`. Collectors allow to collect the objects. A `Collector` is added to `CollectorContext` with a name and corresponding `Collector` instance.
14*78c4dd6aSAndroid Build Coastguard Worker
15*78c4dd6aSAndroid Build Coastguard Worker```java
16*78c4dd6aSAndroid Build Coastguard WorkerCollectorContext collectorContext = executionContext.getCollectorContext();
17*78c4dd6aSAndroid Build Coastguard WorkercollectorContext.add(SAMPLE_COLLECTOR_NAME, new Collector<List<String>>() {
18*78c4dd6aSAndroid Build Coastguard Worker    @Override
19*78c4dd6aSAndroid Build Coastguard Worker    public List<String> collect() {
20*78c4dd6aSAndroid Build Coastguard Worker        List<String> references = new ArrayList<String>();
21*78c4dd6aSAndroid Build Coastguard Worker        references.add(getDatasourceMap().get(node.textValue()));
22*78c4dd6aSAndroid Build Coastguard Worker        return references;
23*78c4dd6aSAndroid Build Coastguard Worker    }
24*78c4dd6aSAndroid Build Coastguard Worker});
25*78c4dd6aSAndroid Build Coastguard Worker```
26*78c4dd6aSAndroid Build Coastguard Worker
27*78c4dd6aSAndroid Build Coastguard WorkerHowever there might be use cases where we want to add a simple Object like String, Integer, etc, into the Context. This can be done the same way a collector is added to the context.
28*78c4dd6aSAndroid Build Coastguard Worker
29*78c4dd6aSAndroid Build Coastguard Worker```java
30*78c4dd6aSAndroid Build Coastguard WorkerCollectorContext collectorContext = executionContext.getCollectorContext();
31*78c4dd6aSAndroid Build Coastguard WorkercollectorContext.add(SAMPLE_COLLECTOR, "sample-string")
32*78c4dd6aSAndroid Build Coastguard Worker```
33*78c4dd6aSAndroid Build Coastguard Worker
34*78c4dd6aSAndroid Build Coastguard WorkerTo use the `CollectorContext` while validating, the `validateAndCollect` method has to be invoked on the `JsonSchema` class.
35*78c4dd6aSAndroid Build Coastguard WorkerThis method returns a `ValidationResult` that contains the errors encountered during validation and a `ExecutionContext` instance that contains the `CollectorContext`.
36*78c4dd6aSAndroid Build Coastguard WorkerObjects constructed by collectors or directly added to `CollectorContext` can be retrieved from `CollectorContext` by using the name they were added with.
37*78c4dd6aSAndroid Build Coastguard Worker
38*78c4dd6aSAndroid Build Coastguard WorkerTo collect across multiple validation runs, the `CollectorContext` needs to be explicitly reused by passing the `ExecutionContext` as a parameter to the validation.
39*78c4dd6aSAndroid Build Coastguard Worker
40*78c4dd6aSAndroid Build Coastguard Worker```java
41*78c4dd6aSAndroid Build Coastguard WorkerValidationResult validationResult = jsonSchema.validateAndCollect(jsonNode);
42*78c4dd6aSAndroid Build Coastguard WorkerExecutionContext executionContext = validationResult.getExecutionContext();
43*78c4dd6aSAndroid Build Coastguard WorkerCollectorContext collectorContext = executionContext.getCollectorContext();
44*78c4dd6aSAndroid Build Coastguard WorkerList<String> contextValue = (List<String>) collectorContext.get(SAMPLE_COLLECTOR);
45*78c4dd6aSAndroid Build Coastguard Worker
46*78c4dd6aSAndroid Build Coastguard Worker// Do something with contextValue
47*78c4dd6aSAndroid Build Coastguard Worker...
48*78c4dd6aSAndroid Build Coastguard Worker
49*78c4dd6aSAndroid Build Coastguard Worker// To collect more information for subsequent runs reuse the context
50*78c4dd6aSAndroid Build Coastguard WorkervalidationResult = jsonSchema.validateAndCollect(executionContext, jsonNode);
51*78c4dd6aSAndroid Build Coastguard Worker```
52*78c4dd6aSAndroid Build Coastguard Worker
53*78c4dd6aSAndroid Build Coastguard WorkerThere might be use cases where a collector needs to collect the data at multiple touch points. For example one use case might be collecting data in a validator and a formatter. If you are using a `Collector` rather than a `Object`, the combine method of the `Collector` allows to define how we want to combine the data into existing `Collector`. `CollectorContext` `combineWithCollector` method calls the combine method on the `Collector`. User just needs to call the `CollectorContext` `combineWithCollector` method every time some data needs to merged into existing `Collector`. The `collect` method on the `Collector` is called by the framework at the end of validation to return the data that was collected.
54*78c4dd6aSAndroid Build Coastguard Worker
55*78c4dd6aSAndroid Build Coastguard Worker```java
56*78c4dd6aSAndroid Build Coastguard Workerclass CustomCollector implements Collector<List<String>> {
57*78c4dd6aSAndroid Build Coastguard Worker
58*78c4dd6aSAndroid Build Coastguard Worker    List<String> returnList = new ArrayList<>();
59*78c4dd6aSAndroid Build Coastguard Worker
60*78c4dd6aSAndroid Build Coastguard Worker    private Map<String, String> referenceMap = null;
61*78c4dd6aSAndroid Build Coastguard Worker
62*78c4dd6aSAndroid Build Coastguard Worker    public CustomCollector() {
63*78c4dd6aSAndroid Build Coastguard Worker        referenceMap = getDatasourceMap();
64*78c4dd6aSAndroid Build Coastguard Worker    }
65*78c4dd6aSAndroid Build Coastguard Worker
66*78c4dd6aSAndroid Build Coastguard Worker    @Override
67*78c4dd6aSAndroid Build Coastguard Worker    public List<String> collect() {
68*78c4dd6aSAndroid Build Coastguard Worker        return returnList;
69*78c4dd6aSAndroid Build Coastguard Worker    }
70*78c4dd6aSAndroid Build Coastguard Worker
71*78c4dd6aSAndroid Build Coastguard Worker    @Override
72*78c4dd6aSAndroid Build Coastguard Worker    public void combine(Object object) {
73*78c4dd6aSAndroid Build Coastguard Worker        returnList.add(referenceMap.get((String) object));
74*78c4dd6aSAndroid Build Coastguard Worker    }
75*78c4dd6aSAndroid Build Coastguard Worker}
76*78c4dd6aSAndroid Build Coastguard Worker
77*78c4dd6aSAndroid Build Coastguard WorkerCollectorContext collectorContext = executionContext.getCollectorContext();
78*78c4dd6aSAndroid Build Coastguard Workerif (collectorContext.get(SAMPLE_COLLECTOR) == null) {
79*78c4dd6aSAndroid Build Coastguard Worker    collectorContext.add(SAMPLE_COLLECTOR, new CustomCollector());
80*78c4dd6aSAndroid Build Coastguard Worker}
81*78c4dd6aSAndroid Build Coastguard WorkercollectorContext.combineWithCollector(SAMPLE_COLLECTOR, node.textValue());
82*78c4dd6aSAndroid Build Coastguard Worker
83*78c4dd6aSAndroid Build Coastguard Worker```
84*78c4dd6aSAndroid Build Coastguard Worker
85*78c4dd6aSAndroid Build Coastguard WorkerOne important thing to note when using Collectors is if we call get method on `CollectorContext` before the validation is complete, we would get back a `Collector` instance that was added to `CollectorContext`.
86*78c4dd6aSAndroid Build Coastguard Worker
87*78c4dd6aSAndroid Build Coastguard Worker```java
88*78c4dd6aSAndroid Build Coastguard Worker// Returns Collector before validation is done.
89*78c4dd6aSAndroid Build Coastguard WorkerCollector<List<String>> collector = collectorContext.get(SAMPLE_COLLECTOR);
90*78c4dd6aSAndroid Build Coastguard Worker
91*78c4dd6aSAndroid Build Coastguard Worker// Returns data collected by Collector after the validation is done.
92*78c4dd6aSAndroid Build Coastguard WorkerList<String> data = collectorContext.get(SAMPLE_COLLECTOR);
93*78c4dd6aSAndroid Build Coastguard Worker
94*78c4dd6aSAndroid Build Coastguard Worker```
95*78c4dd6aSAndroid Build Coastguard Worker
96*78c4dd6aSAndroid Build Coastguard WorkerIf you are using simple objects and if the data needs to be collected from multiple touch points, logic is straightforward as shown.
97*78c4dd6aSAndroid Build Coastguard Worker
98*78c4dd6aSAndroid Build Coastguard Worker```java
99*78c4dd6aSAndroid Build Coastguard WorkerCollectorContext collectorContext = executionContext.getCollectorContext();
100*78c4dd6aSAndroid Build Coastguard Worker// If collector name is not added to context add one.
101*78c4dd6aSAndroid Build Coastguard Workerif (collectorContext.get(SAMPLE_COLLECTOR) == null) {
102*78c4dd6aSAndroid Build Coastguard Worker    collectorContext.add(SAMPLE_COLLECTOR, new ArrayList<String>());
103*78c4dd6aSAndroid Build Coastguard Worker}
104*78c4dd6aSAndroid Build Coastguard Worker// In this case we are adding a list to CollectorContext.
105*78c4dd6aSAndroid Build Coastguard WorkerList<String> returnList = (List<String>) collectorContext.get(SAMPLE_COLLECTOR);
106*78c4dd6aSAndroid Build Coastguard Worker
107*78c4dd6aSAndroid Build Coastguard Worker```