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```