7
7
import com .cerner .bunsen .definitions .HapiConverter ;
8
8
import com .cerner .bunsen .definitions .HapiConverter .HapiObjectConverter ;
9
9
import com .cerner .bunsen .definitions .StructureDefinitions ;
10
+ import com .cerner .bunsen .exception .ProfileException ;
11
+ import com .google .common .base .Preconditions ;
12
+ import java .util .ArrayList ;
10
13
import java .util .Collections ;
11
14
import java .util .HashMap ;
15
+ import java .util .Iterator ;
12
16
import java .util .List ;
13
17
import java .util .Map ;
14
18
import java .util .Map .Entry ;
15
19
import java .util .stream .Collectors ;
16
20
import org .apache .avro .Schema ;
17
21
import org .apache .avro .Schema .Field ;
18
22
import org .apache .avro .generic .IndexedRecord ;
23
+ import org .apache .commons .collections .CollectionUtils ;
19
24
import org .hl7 .fhir .instance .model .api .IBaseResource ;
20
25
21
26
/** Converter to change HAPI objects into Avro structures and vice versa. */
@@ -27,7 +32,6 @@ public class AvroConverter {
27
32
28
33
private AvroConverter (
29
34
HapiConverter <Schema > hapiToAvroConverter , RuntimeResourceDefinition ... resources ) {
30
-
31
35
this .hapiToAvroConverter = hapiToAvroConverter ;
32
36
this .avroToHapiConverter = (HapiObjectConverter ) hapiToAvroConverter .toHapiConverter (resources );
33
37
}
@@ -37,7 +41,8 @@ private static AvroConverter visitResource(
37
41
StructureDefinitions structureDefinitions ,
38
42
String resourceTypeUrl ,
39
43
List <String > containedResourceTypeUrls ,
40
- Map <String , HapiConverter <Schema >> compositeConverters ) {
44
+ Map <String , HapiConverter <Schema >> compositeConverters ,
45
+ int recursiveDepth ) {
41
46
42
47
FhirVersionEnum fhirVersion = context .getVersion ().getVersion ();
43
48
@@ -58,7 +63,10 @@ private static AvroConverter visitResource(
58
63
59
64
DefinitionToAvroVisitor visitor =
60
65
new DefinitionToAvroVisitor (
61
- structureDefinitions .conversionSupport (), basePackage , compositeConverters );
66
+ structureDefinitions .conversionSupport (),
67
+ basePackage ,
68
+ compositeConverters ,
69
+ recursiveDepth );
62
70
63
71
HapiConverter <Schema > converter =
64
72
structureDefinitions .transform (visitor , resourceTypeUrl , containedResourceTypeUrls );
@@ -97,7 +105,7 @@ private static AvroConverter visitResource(
97
105
* @return a list of Avro schemas
98
106
*/
99
107
public static List <Schema > generateSchemas (
100
- FhirContext context , Map <String , List <String >> resourceTypeUrls ) {
108
+ FhirContext context , Map <String , List <String >> resourceTypeUrls , int recursiveDepth ) {
101
109
102
110
StructureDefinitions structureDefinitions = StructureDefinitions .create (context );
103
111
@@ -110,7 +118,8 @@ public static List<Schema> generateSchemas(
110
118
structureDefinitions ,
111
119
resourceTypeUrlEntry .getKey (),
112
120
resourceTypeUrlEntry .getValue (),
113
- converters );
121
+ converters ,
122
+ recursiveDepth );
114
123
}
115
124
116
125
return converters .values ().stream ()
@@ -126,11 +135,40 @@ public static List<Schema> generateSchemas(
126
135
*
127
136
* @param context the FHIR context
128
137
* @param resourceTypeUrl the URL of the resource type
138
+ * @param recursiveDepth the maximum recursive depth to stop when converting a FHIR
139
+ * StructureDefinition to an Avro schema.
129
140
* @return an Avro converter instance.
130
141
*/
131
- public static AvroConverter forResource (FhirContext context , String resourceTypeUrl ) {
142
+ public static AvroConverter forResource (
143
+ FhirContext context , String resourceTypeUrl , int recursiveDepth ) {
132
144
133
- return forResource (context , resourceTypeUrl , Collections .emptyList ());
145
+ return forResource (context , resourceTypeUrl , Collections .emptyList (), recursiveDepth );
146
+ }
147
+
148
+ /**
149
+ * Similar to {@link #forResource(FhirContext, String, int)} this method returns an Avro
150
+ * converter, but the returned Avro converter is a union of all the converters for the given
151
+ * resourceTypeUrls, the union is formed by merging all the fields in each converter.
152
+ *
153
+ * @param context the FHIR context
154
+ * @param resourceTypeUrls the list of resource type profile urls. The resourceTypeUrl can either
155
+ * be a relative URL for a base resource (e.g., "Condition" or "Observation"), or a URL
156
+ * identifying the structure definition for a given profile, such as
157
+ * "http://hl7.org/fhir/us/core/StructureDefinition/us-core-patient".
158
+ * @param recursiveDepth the maximum recursive depth to stop when converting a FHIR
159
+ * StructureDefinition to an Avro schema.
160
+ * @return the merged Avro converter
161
+ */
162
+ public static AvroConverter forResources (
163
+ FhirContext context , List <String > resourceTypeUrls , int recursiveDepth )
164
+ throws ProfileException {
165
+ List <AvroConverter > avroConverters = new ArrayList <>();
166
+ for (String resourceTypeUrl : resourceTypeUrls ) {
167
+ AvroConverter avroConverter =
168
+ forResource (context , resourceTypeUrl , Collections .emptyList (), recursiveDepth );
169
+ avroConverters .add (avroConverter );
170
+ }
171
+ return mergeAvroConverters (avroConverters , context );
134
172
}
135
173
136
174
/**
@@ -145,15 +183,25 @@ public static AvroConverter forResource(FhirContext context, String resourceType
145
183
* @param context the FHIR context
146
184
* @param resourceTypeUrl the URL of the resource type
147
185
* @param containedResourceTypeUrls the list of URLs of contained resource types
186
+ * @param recursiveDepth the maximum recursive depth to stop when converting a FHIR
187
+ * StructureDefinition to an Avro schema.
148
188
* @return an Avro converter instance.
149
189
*/
150
190
public static AvroConverter forResource (
151
- FhirContext context , String resourceTypeUrl , List <String > containedResourceTypeUrls ) {
191
+ FhirContext context ,
192
+ String resourceTypeUrl ,
193
+ List <String > containedResourceTypeUrls ,
194
+ int recursiveDepth ) {
152
195
153
196
StructureDefinitions structureDefinitions = StructureDefinitions .create (context );
154
197
155
198
return visitResource (
156
- context , structureDefinitions , resourceTypeUrl , containedResourceTypeUrls , new HashMap <>());
199
+ context ,
200
+ structureDefinitions ,
201
+ resourceTypeUrl ,
202
+ containedResourceTypeUrls ,
203
+ new HashMap <>(),
204
+ recursiveDepth );
157
205
}
158
206
159
207
/**
@@ -196,4 +244,29 @@ public Schema getSchema() {
196
244
public String getResourceType () {
197
245
return hapiToAvroConverter .getElementType ();
198
246
}
247
+
248
+ /**
249
+ * Merges all the given list of avroConverters to create a single AvroConverter which is a union
250
+ * of all the fields in the list of avroConverters
251
+ */
252
+ private static AvroConverter mergeAvroConverters (
253
+ List <AvroConverter > avroConverters , FhirContext context ) throws ProfileException {
254
+ Preconditions .checkArgument (
255
+ !CollectionUtils .isEmpty (avroConverters ), "AvroConverter list cannot be empty for merging" );
256
+ Iterator <AvroConverter > iterator = avroConverters .iterator ();
257
+ AvroConverter mergedConverter = iterator .next ();
258
+ while (iterator .hasNext ()) {
259
+ mergedConverter = mergeAvroConverters (mergedConverter , iterator .next (), context );
260
+ }
261
+ return mergedConverter ;
262
+ }
263
+
264
+ private static AvroConverter mergeAvroConverters (
265
+ AvroConverter left , AvroConverter right , FhirContext context ) throws ProfileException {
266
+ HapiConverter <Schema > mergedConverter =
267
+ left .hapiToAvroConverter .merge (right .hapiToAvroConverter );
268
+ RuntimeResourceDefinition [] resources = new RuntimeResourceDefinition [1 ];
269
+ resources [0 ] = context .getResourceDefinition (mergedConverter .getElementType ());
270
+ return new AvroConverter (mergedConverter , resources );
271
+ }
199
272
}
0 commit comments