View Javadoc
1   /*
2    * Copyright 2009 the original author or authors.
3    * 
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *     http://www.apache.org/licenses/LICENSE-2.0
9    *     
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package org.openehealth.ipf.commons.ihe.xds.core.transform.requests.query;
17  
18  import ca.uhn.hl7v2.model.Composite;
19  import org.apache.commons.lang3.StringUtils;
20  import org.openehealth.ipf.commons.ihe.xds.core.ebxml.EbXMLAdhocQueryRequest;
21  import org.openehealth.ipf.commons.ihe.xds.core.ebxml.EbXMLSlot;
22  import org.openehealth.ipf.commons.ihe.xds.core.metadata.*;
23  import org.openehealth.ipf.commons.ihe.xds.core.requests.query.QueryList;
24  import org.openehealth.ipf.commons.ihe.xds.core.transform.requests.QueryParameter;
25  import org.openehealth.ipf.commons.ihe.xds.core.validate.ValidationMessage;
26  import org.openehealth.ipf.commons.ihe.xds.core.validate.XDSMetaDataException;
27  
28  import java.util.ArrayList;
29  import java.util.List;
30  import java.util.stream.Collectors;
31  
32  import static org.apache.commons.lang3.StringUtils.isEmpty;
33  import static org.apache.commons.lang3.StringUtils.isNotBlank;
34  import static org.apache.commons.lang3.Validate.notNull;
35  import static org.apache.commons.lang3.Validate.noNullElements;
36  
37  /**
38   * Wrapper class for ebXML query request to simplify access to slots.
39   * <p>
40   * This class ensures that the various encoding rules of query parameter
41   * values are met.
42   * <p>
43   * Note that this class is only used for ebXML 3.0! 
44   * @author Jens Riemschneider
45   * @author Dmytro Rud
46   */
47  public class QuerySlotHelper {
48  
49      private final EbXMLAdhocQueryRequest ebXML;
50  
51      /**
52       * Constructs the wrapper.
53       * @param ebXML
54       *          the wrapped object.
55       */
56      public QuerySlotHelper(EbXMLAdhocQueryRequest ebXML) {
57          notNull(ebXML, "ebXML cannot be null");
58          this.ebXML = ebXML;
59      }
60  
61      /**
62       * Retrieves a string-valued parameter from a slot.
63       * @param param
64       *          the parameter.
65       * @return the string value.
66       */
67      public String toString(QueryParameter param) {
68          String value = ebXML.getSingleSlotValue(param.getSlotName());
69          return decodeString(value);
70      }
71      
72      /**
73       * Stores a string-valued parameter into a slot.
74       * @param param
75       *          the parameter.
76       * @param value
77       *          the string value.
78       */
79      public void fromString(QueryParameter param, String value) {
80          if (value != null) {
81              ebXML.addSlot(param.getSlotName(), encodeAsString(value));
82          }
83      }
84  
85      /**
86       * Stores a list of codes into a slot. 
87       * @param param
88       *          the parameter.
89       * @param codes
90       *          the list of codes.
91       */
92      public void fromCode(QueryParameter param, List<Code> codes) {
93          if (codes == null) {
94              return;
95          }
96          
97          List<String> slotValues = new ArrayList<>();
98          for (Code code : codes) {
99              String hl7CE = Hl7v2Based.render(code);
100             slotValues.add(encodeAsStringList(hl7CE));
101         }
102         ebXML.addSlot(param.getSlotName(), slotValues.toArray(new String[slotValues.size()]));
103     }
104 
105     /**
106      * Stores a code list with AND/OR semantics into a set of slots with the same name.
107      * @param param
108      *          standard query parameter (implies the name of the slots).
109      * @param queryList
110      *          the list of codes.
111      */
112     public void fromCode(QueryParameter param, QueryList<Code> queryList) {
113         if (queryList == null) {
114             return;
115         }
116         
117         for (List<Code> codes : queryList.getOuterList()) {
118             fromCode(param, codes);
119         }
120     }
121 
122     private QueryList<Code> toCodeQueryList(QueryParameter param) {
123         List<EbXMLSlot> slots = ebXML.getSlots(param.getSlotName());
124         if (slots.isEmpty()) {
125             return null;
126         }
127         
128         QueryList<Code> queryList = new QueryList<>();
129         for (EbXMLSlot slot : slots) {
130             List<Code> innerList = toCode(slot.getValueList());
131             queryList.getOuterList().add(innerList);
132         }
133         return queryList;
134     }
135 
136     /**
137      * Retrieves a string list with AND/OR semantics from a set of slots with the same name.
138      * @param param
139      *          standard query parameter (implies the name of the slots).
140      * @return the string list.
141      */
142     public QueryList<String> toStringQueryList(QueryParameter param) {
143         return toStringQueryList(param.getSlotName());
144     }
145 
146     /**
147      * Retrieves a string list with AND/OR semantics from a set of slots with the same name.
148      * @param slotName
149      *          name of the source slots, may correspond to either
150      *          a standard query parameter or an extra parameter.
151      * @return the string list.
152      */
153     public QueryList<String> toStringQueryList(String slotName) {
154         if (isEmpty(slotName)) {
155             return null;
156         }
157 
158         List<EbXMLSlot> slots = ebXML.getSlots(slotName);
159         if (slots.isEmpty()) {
160             return null;
161         }
162         
163         QueryList<String> queryList = new QueryList<>();
164         for (EbXMLSlot slot : slots) {
165             List<String> innerList = new ArrayList<>();
166             for (String slotValue : slot.getValueList()) {            
167                 innerList.addAll(decodeStringList(slotValue));
168             }
169             queryList.getOuterList().add(innerList);
170         }
171         return queryList;
172     }
173     
174     /**
175      * Stores a list of strings into a slot.
176      * @param param
177      *          standard query parameter (implies the name of the slots).
178      * @param values
179      *          the string list.
180      */
181     public void fromStringList(QueryParameter param, List<String> values) {
182         fromStringList(param.getSlotName(), values);
183     }
184 
185     /**
186      * Stores a list of strings into a slot.
187      * @param slotName
188      *          name of the target slot, may correspond to either
189      *          a standard query parameter or an extra parameter.
190      * @param values
191      *          the string list.
192      */
193     public void fromStringList(String slotName, List<String> values) {
194         if (isEmpty(slotName) || (values == null)) {
195             return;
196         }
197         
198         String[] slotValues = values.stream()
199                 .map(QuerySlotHelper::encodeAsStringList)
200                 .toArray(String[]::new);
201         ebXML.addSlot(slotName, slotValues);
202     }
203 
204     /**
205      * Stores a string list with AND/OR semantics into a set of slots with the same name.
206      * @param param
207      *          standard query parameter (implies the name of the slots).
208      * @param queryList
209      *          the list of strings.
210      */
211     public void fromStringList(QueryParameter param, QueryList<String> queryList) {
212         fromStringList(param.getSlotName(), queryList);
213     }
214 
215     /**
216      * Stores a string list with AND/OR semantics into a set of slots with the same name.
217      * @param slotName
218      *          name of the target slots, may correspond to either
219      *          a standard query parameter or an extra parameter.
220      * @param queryList
221      *          the list of strings.
222      */
223     public void fromStringList(String slotName, QueryList<String> queryList) {
224         if (isEmpty(slotName) || (queryList == null)) {
225             return;
226         }
227 
228         for (List<String> list : queryList.getOuterList()) {
229             fromStringList(slotName, list);
230         }
231     }
232 
233     /**
234      * Retrieves a list of strings from a slot.
235      * @param param
236      *          the parameter.
237      * @return the string list.
238      */
239     public List<String> toStringList(QueryParameter param) {
240         List<String> slotValues = ebXML.getSlotValues(param.getSlotName());
241         if (slotValues.isEmpty()) {
242             return null;
243         }
244         
245         List<String> values = new ArrayList<>();
246         for (String slotValue : slotValues) {            
247             values.addAll(decodeStringList(slotValue));
248         }
249         return values;
250     }
251 
252 
253     /**
254      * Stores a list of patient IDs into a slot.
255      * @param param
256      *          the parameter.
257      * @param values
258      *          the patient ID list.
259      */
260     public void fromPatientIdList(QueryParameter param, List<Identifiable> values) {
261         if (values == null) {
262             return;
263         }
264 
265         String[] slotValues = values.stream()
266                 .map(value -> encodeAsStringList(Hl7v2Based.render(value)))
267                 .toArray(String[]::new);
268         ebXML.addSlot(param.getSlotName(), slotValues);
269     }
270 
271     /**
272      * Retrieves a list of patient IDs from a slot.
273      * @param param
274      *          the parameter.
275      * @return the patient ID list.
276      */
277     public List<Identifiable> toPatientIdList(QueryParameter param) {
278         List<String> values = toStringList(param);
279         if (values == null) {
280             return null;
281         }
282 
283         List<Identifiable> patientIds = new ArrayList<>();
284         for (String value : values) {
285             patientIds.add(Hl7v2Based.parse(value, Identifiable.class));
286         }
287         return patientIds;
288     }
289 
290     /**
291      * Retrieves a list of codes from a slot.
292      * @param param
293      *          the parameter.
294      * @return the codes.
295      */
296     public List<Code> toCodeList(QueryParameter param) {
297         return toCode(ebXML.getSlotValues(param.getSlotName()));
298     }
299 
300     /**
301      * Retrieves a code list with AND/OR semantics from a set of slots with the same name.
302      * @param param
303      *          standard query parameter (implies the name of the slots).
304      * @param schemeParam
305      *          the code scheme parameter.
306      * @return the codes.
307      */
308     public QueryList<Code> toCodeQueryList(QueryParameter param, QueryParameter schemeParam) {
309         QueryList<Code> codes = toCodeQueryList(param);
310         if (codes == null) {
311             return null;            
312         }
313         
314         QueryList<String> schemes = toStringQueryList(schemeParam);
315         if (schemes != null) {
316             List<List<String>> schemesOuter = schemes.getOuterList();
317             List<List<Code>> codesOuter = codes.getOuterList();
318             for (int outer = 0; outer < schemesOuter.size() && outer < codesOuter.size(); ++outer) {
319                 List<String> schemesInner = schemesOuter.get(outer);
320                 List<Code> codesInner = codesOuter.get(outer);
321                 for (int inner = 0; inner < schemesInner.size() && inner < codesInner.size(); ++inner) {
322                     codesInner.get(inner).setSchemeName(schemesInner.get(inner));
323                 }
324             }
325         }
326         return codes;
327     }
328     
329     /**
330      * Stores a numbered parameter into a slot.
331      * @param param
332      *          the parameter.
333      * @param value
334      *          the value.
335      */
336     public void fromNumber(QueryParameter param, String value) {
337         ebXML.addSlot(param.getSlotName(), value);
338     }
339     
340     /**
341      * Retrieves a numbered parameter from a slot.
342      * @param param
343      *          the parameter.
344      * @return the value.
345      */
346     public String toNumber(QueryParameter param) {
347         return ebXML.getSingleSlotValue(param.getSlotName());
348     }
349 
350     /**
351      * Stores a status parameter into a slot.
352      * @param param
353      *          the parameter.
354      * @param status
355      *          the list of status values.
356      */
357     public void fromStatus(QueryParameter param, List<AvailabilityStatus> status) {
358         if (status == null) {
359             return;
360         }
361         List<String> opcodes = status.stream()
362                 .map(AvailabilityStatus::toQueryOpcode)
363                 .collect(Collectors.toList());
364         fromStringList(param, opcodes);
365     }
366 
367     /**
368      * Retrieves a status parameter from a slot.
369      * @param param
370      *          the parameter.
371      * @return the list of status values.
372      */
373     public List<AvailabilityStatus> toStatus(QueryParameter param) {
374         List<String> opcodes = toStringList(param);
375         if (opcodes == null) {
376             return null;
377         }
378 
379         List<AvailabilityStatus> list = new ArrayList<>();
380         for (String opcode : opcodes) {
381             AvailabilityStatus status = AvailabilityStatus.valueOfOpcode(opcode);
382             if (status != null) {
383                 list.add(status);
384             }
385         }
386         return list;
387     }
388 
389     /**
390      * Stores an association parameter into a slot.
391      * @param param
392      *          the parameter.
393      * @param associationTypes
394      *          the list of association types.
395      */
396     public void fromAssociationType(QueryParameter param, List<AssociationType> associationTypes) {
397         if (associationTypes == null) {
398             return;
399         }
400         
401         List<String> opcodes = associationTypes.stream()
402                 .map(type -> AssociationType.getOpcode30(type))
403                 .collect(Collectors.toList());
404         fromStringList(param, opcodes);
405     }
406     
407     /**
408      * Retrieves an association parameter from a slot. 
409      * @param param
410      *          the parameter.
411      * @return the list of association types.
412      */
413     public List<AssociationType> toAssociationType(QueryParameter param) {
414         List<String> opcodes = toStringList(param);
415         if (opcodes == null) {
416             return null;
417         }
418 
419         return opcodes.stream()
420                 .map(AssociationType::valueOfOpcode30)
421                 .collect(Collectors.toList());
422     }
423 
424     public void fromDocumentEntryType(QueryParameter param, List<DocumentEntryType> documentEntryTypes) {
425         if (documentEntryTypes == null) {
426             return;
427         }
428 
429         List<String> uuids = documentEntryTypes.stream()
430                 .map(DocumentEntryType::toUuid)
431                 .collect(Collectors.toList());
432         fromStringList(param, uuids);
433     }
434 
435     public List<DocumentEntryType> toDocumentEntryType(QueryParameter param) {
436         List<String> uuids = toStringList(param);
437         if (uuids == null) {
438             return null;
439         }
440 
441        return uuids.stream()
442                .map(DocumentEntryType::valueOfUuid)
443                .collect(Collectors.toList());
444     }
445 
446     public static List<Code> toCode(List<String> slotValues) {
447         if (slotValues.isEmpty()) {
448             return null;
449         }
450         
451         List<Code> codes = new ArrayList<>();
452         for (String slotValue : slotValues) {
453             for (String hl7CE : decodeStringList(slotValue)) {
454                 Code code = Hl7v2Based.parse(hl7CE, Code.class);
455                 if (code == null || StringUtils.isEmpty(code.getCode()) || StringUtils.isEmpty(code.getSchemeName())) {
456                     throw new XDSMetaDataException(ValidationMessage.INVALID_QUERY_PARAMETER_VALUE, hl7CE);
457                 }
458                 codes.add(code);
459             }
460         }
461         return codes;
462     }
463 
464 
465     /**
466      * Stores a status parameter into a slot.
467      * @param param
468      *          the parameter.
469      * @param status
470      *          the list of documentAvailability values.
471      */
472     public void fromDocumentAvailability(QueryParameter param, List<DocumentAvailability> status) {
473         if (status == null) {
474             return;
475         }
476 
477         List<String> opcodes = status.stream()
478                 .map(DocumentAvailability::toFullQualifiedOpcode)
479                 .collect(Collectors.toList());
480         fromStringList(param, opcodes);
481     }
482 
483     /**
484      * Retrieves a status parameter from a slot.
485      * @param param
486      *          the parameter.
487      * @return the list of documentAvailability values.
488      */
489     public List<DocumentAvailability> toDocumentAvailability(QueryParameter param) {
490         List<String> opcodes = toStringList(param);
491         if (opcodes == null) {
492             return null;
493         }
494 
495         List<DocumentAvailability> list = new ArrayList<>();
496         for (String opcode : opcodes) {
497             DocumentAvailability availability = DocumentAvailability.valueOfOpcode(opcode);
498             if (availability != null) {
499                 list.add(availability);
500             }
501         }
502         return list;
503     }
504 
505     /**
506      * Stores a numbered parameter into a slot.
507      * @param param
508      *          the parameter.
509      * @param value
510      *          the value.
511      */
512     public void fromInteger(QueryParameter param, Integer value) {
513         if (value == null){
514             return;
515         }
516         ebXML.addSlot(param.getSlotName(), String.valueOf(value));
517     }
518 
519     /**
520      * Retrieves a numbered parameter from a slot.
521      * @param param
522      *          the parameter.
523      * @return the value.
524      */
525     public Integer toInteger(QueryParameter param) {
526         Integer result = null;
527         try {
528             String slotValue = ebXML.getSingleSlotValue(param.getSlotName());
529             if (StringUtils.isNotBlank(slotValue)) {
530                 result = Integer.valueOf(slotValue);
531             }
532         } catch (NumberFormatException nfe){
533             // ok, return null
534         }
535         return result;
536     }
537     
538     public static String encodeAsString(String value) {
539         if (value == null) {
540             return null;
541         }
542         return "'" + value.replace("'", "''") + "'";
543     }
544 
545     public static String decodeString(String value) {
546         if (value == null) {
547             return null;
548         }
549         
550         if (value.startsWith("'") && value.endsWith("'")) {
551             value = value.substring(1, value.length() - 1);
552         }
553         return value.replaceAll("''", "'");
554     }
555 
556     public static String encodeAsStringList(String value) {
557         if (value == null) {
558             return null;
559         }
560         return "('" + value.replace("'", "''") + "')";
561     }
562 
563     public static List<String> decodeStringList(String list) {
564         if (list == null) {
565             return null;
566         }
567 
568         list = list.trim();
569         if (list.startsWith("(")) {
570             list = list.substring(1);
571         }
572         if (list.endsWith(")")) {
573             list = list.substring(0, list.length() - 1);
574         }
575         
576         List<String> values = new ArrayList<>();
577         for (String value: list.split(",")){
578             String decodedValue = isNotBlank(value)? decodeString(value.trim()): "";
579             values.add(decodedValue);
580         }
581         return values;
582     }
583 
584     public static <T extends Hl7v2Based> QueryList<String> render(QueryList<T> source) {
585         if (source == null) {
586             return null;
587         }
588         noNullElements(source.getOuterList(), "outer list cannot contain NULL elements");
589         QueryList<String> target = new QueryList<>();
590         source.getOuterList().forEach(list -> target.getOuterList().add(render(list)));
591         return target;
592     }
593 
594     public static <T extends Hl7v2Based> List<String> render(List<T> source) {
595         if (source == null) {
596             return null;
597         }
598         noNullElements(source, "list cannot contain NULL elements");
599         return source.stream()
600                 .map(Hl7v2Based::render)
601                 .collect(Collectors.toList());
602     }
603 
604     public static <C extends Composite, T extends Hl7v2Based<C>> QueryList<T> parse(
605             QueryList<String> source,
606             Class<T> targetClass)
607     {
608         if (source == null) {
609             return null;
610         }
611         noNullElements(source.getOuterList(), "outer list cannot contain NULL elements");
612         QueryList<T> target = new QueryList<>();
613         source.getOuterList().forEach(list -> target.getOuterList().add(parse(list, targetClass)));
614         return target;
615     }
616 
617     public static <C extends Composite, T extends Hl7v2Based<C>> List<T> parse(
618             List<String> source,
619             Class<T> targetClass)
620     {
621         if (source == null) {
622             return null;
623         }
624         noNullElements(source, "list cannot contain NULL elements");
625         return source.stream()
626                 .map(value -> Hl7v2Based.parse(value, targetClass))
627                 .collect(Collectors.toList());
628     }
629 
630 }