001package org.hl7.fhir.r5.utils;
002
003import java.util.List;
004
005import org.hl7.fhir.r5.extensions.ExtensionDefinitions;
006import org.hl7.fhir.r5.extensions.ExtensionUtilities;
007import org.hl7.fhir.r5.model.CodeType;
008
009/*
010  Copyright (c) 2011+, HL7, Inc.
011  All rights reserved.
012  
013  Redistribution and use in source and binary forms, with or without modification, 
014  are permitted provided that the following conditions are met:
015    
016   * Redistributions of source code must retain the above copyright notice, this 
017     list of conditions and the following disclaimer.
018   * Redistributions in binary form must reproduce the above copyright notice, 
019     this list of conditions and the following disclaimer in the documentation 
020     and/or other materials provided with the distribution.
021   * Neither the name of HL7 nor the names of its contributors may be used to 
022     endorse or promote products derived from this software without specific 
023     prior written permission.
024  
025  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 
026  ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 
027  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 
028  IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 
029  INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 
030  NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 
031  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 
032  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
033  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
034  POSSIBILITY OF SUCH DAMAGE.
035  
036 */
037
038
039
040import org.hl7.fhir.r5.model.CodeableConcept;
041import org.hl7.fhir.r5.model.Extension;
042import org.hl7.fhir.r5.model.IntegerType;
043import org.hl7.fhir.r5.model.Narrative.NarrativeStatus;
044import org.hl7.fhir.r5.model.OperationOutcome;
045import org.hl7.fhir.r5.model.OperationOutcome.IssueSeverity;
046import org.hl7.fhir.r5.model.OperationOutcome.IssueType;
047import org.hl7.fhir.r5.model.OperationOutcome.OperationOutcomeIssueComponent;
048import org.hl7.fhir.r5.model.StringType;
049import org.hl7.fhir.r5.model.UrlType;
050import org.hl7.fhir.utilities.CommaSeparatedStringBuilder;
051import org.hl7.fhir.utilities.validation.ValidationMessage;
052import org.hl7.fhir.utilities.xhtml.NodeType;
053import org.hl7.fhir.utilities.xhtml.XhtmlNode;
054
055public class OperationOutcomeUtilities {
056
057
058  public static OperationOutcomeIssueComponent convertToIssue(ValidationMessage message, OperationOutcome op) {
059    OperationOutcomeIssueComponent issue = new OperationOutcome.OperationOutcomeIssueComponent();
060    issue.setUserData(UserDataNames.validator_source_vm, message);   
061    issue.setCode(convert(message.getType()));
062    
063    if (message.getLocation() != null) {
064      // message location has a fhirPath in it. We need to populate the expression
065      issue.addExpression(message.getLocation());
066    }
067    // pass through line/col if they're present
068    if (message.getLine() >= 0) {
069      issue.addExtension().setUrl(ExtensionDefinitions.EXT_ISSUE_LINE).setValue(new IntegerType(message.getLine()));
070    }
071    if (message.getCol() >= 0) {
072      issue.addExtension().setUrl(ExtensionDefinitions.EXT_ISSUE_COL).setValue(new IntegerType(message.getCol()));
073    }
074    issue.setSeverity(convert(message.getLevel()));
075    CodeableConcept c = new CodeableConcept();
076    c.setText(message.getMessage());
077    issue.setDetails(c);
078    if (message.getSource() != null) {
079      issue.getExtension().add(ExtensionUtilities.makeIssueSource(message.getSource()));
080    }
081    if (message.getMessageId() != null) {
082      issue.getExtension().add(ExtensionUtilities.makeIssueMessageId(message.getMessageId()));
083    }
084    issue.setUserData(UserDataNames.validator_source_msg, message);
085    return issue;
086  }
087
088  public static IssueSeverity convert(org.hl7.fhir.utilities.validation.ValidationMessage.IssueSeverity level) {
089    switch (level) {
090    case FATAL : return IssueSeverity.FATAL;
091    case ERROR : return IssueSeverity.ERROR;
092    case WARNING : return IssueSeverity.WARNING;
093    case INFORMATION : return IssueSeverity.INFORMATION;
094   case NULL : return IssueSeverity.NULL;
095    }
096    return IssueSeverity.NULL;
097  }
098
099  public static org.hl7.fhir.utilities.validation.ValidationMessage.IssueSeverity convert(IssueSeverity level) {
100    switch (level) {
101    case FATAL : return org.hl7.fhir.utilities.validation.ValidationMessage.IssueSeverity.FATAL;
102    case ERROR : return org.hl7.fhir.utilities.validation.ValidationMessage.IssueSeverity.ERROR;
103    case WARNING : return org.hl7.fhir.utilities.validation.ValidationMessage.IssueSeverity.WARNING;
104    case INFORMATION : return org.hl7.fhir.utilities.validation.ValidationMessage.IssueSeverity.INFORMATION;
105   case NULL : return org.hl7.fhir.utilities.validation.ValidationMessage.IssueSeverity.NULL;
106    }
107    return org.hl7.fhir.utilities.validation.ValidationMessage.IssueSeverity.NULL;
108  }
109
110  private static IssueType convert(org.hl7.fhir.utilities.validation.ValidationMessage.IssueType type) {
111    switch (type) {
112    case INVALID: return IssueType.INVALID; 
113    case STRUCTURE: return IssueType.STRUCTURE;
114    case REQUIRED: return IssueType.REQUIRED;
115    case VALUE: return IssueType.VALUE;
116    case INVARIANT: return IssueType.INVARIANT;
117    case SECURITY: return IssueType.SECURITY;
118    case LOGIN: return IssueType.LOGIN;
119    case UNKNOWN: return IssueType.UNKNOWN;
120    case EXPIRED: return IssueType.EXPIRED;
121    case FORBIDDEN: return IssueType.FORBIDDEN;
122    case SUPPRESSED: return IssueType.SUPPRESSED;
123    case PROCESSING: return IssueType.PROCESSING;
124    case NOTSUPPORTED: return IssueType.NOTSUPPORTED;
125    case DUPLICATE: return IssueType.DUPLICATE;
126    case NOTFOUND: return IssueType.NOTFOUND;
127    case TOOLONG: return IssueType.TOOLONG;
128    case CODEINVALID: return IssueType.CODEINVALID;
129    case EXTENSION: return IssueType.EXTENSION;
130    case TOOCOSTLY: return IssueType.TOOCOSTLY;
131    case BUSINESSRULE: return IssueType.BUSINESSRULE;
132    case CONFLICT: return IssueType.CONFLICT;
133    case INCOMPLETE: return IssueType.INCOMPLETE;
134    case TRANSIENT: return IssueType.TRANSIENT;
135    case LOCKERROR: return IssueType.LOCKERROR;
136    case NOSTORE: return IssueType.NOSTORE;
137    case EXCEPTION: return IssueType.EXCEPTION;
138    case TIMEOUT: return IssueType.TIMEOUT;
139    case THROTTLED: return IssueType.THROTTLED;
140    case INFORMATIONAL: return IssueType.INFORMATIONAL;
141          case NULL: return IssueType.NULL;
142    case DELETED: return IssueType.DELETED;
143    case MULTIPLEMATCHES: return IssueType.MULTIPLEMATCHES;
144    default:
145      return IssueType.NULL;
146    }
147  }
148
149  public static OperationOutcome createOutcome(List<ValidationMessage> messages) {
150    OperationOutcome res = new OperationOutcome();
151    for (ValidationMessage vm : messages) {
152      res.addIssue(convertToIssue(vm, res));
153    }
154    return res;
155  }
156  
157
158  public static OperationOutcomeIssueComponent convertToIssueSimple(ValidationMessage message, OperationOutcome op) {
159    OperationOutcomeIssueComponent issue = new OperationOutcome.OperationOutcomeIssueComponent();
160    issue.setUserData(UserDataNames.validator_source_vm, message);   
161    issue.setCode(convert(message.getType()));
162    
163    if (message.getLocation() != null) {
164      // message location has a fhirPath in it. We need to populate the expression
165      issue.addExpression(message.getLocation());
166    }
167    if (message.getLine() >= 0 && message.getCol() >= 0) {
168      issue.setDiagnostics("["+message.getLine()+","+message.getCol()+"]");
169    }
170    issue.setSeverity(convert(message.getLevel()));
171    CodeableConcept c = new CodeableConcept();
172    c.setText(message.getMessage());
173    issue.setDetails(c);
174    if (message.hasSliceInfo()) {
175      // issue.addExtension(ExtensionDefinitions.EXT_ISSUE_SLICE_INFO, new StringType(errorSummaryForSlicingAsText(message.getSliceInfo())));
176      for (ValidationMessage vm : message.getSliceInfo()) {
177        Extension ext = issue.addExtension();
178        ext.setUrl(ExtensionDefinitions.EXT_ISSUE_INNER_MESSAGE);
179        ext.addExtension("severity", new CodeType(vm.getLevel().toCode()));
180        ext.addExtension("type", new CodeType(vm.getType().toCode()));
181        ext.addExtension("path", new StringType(vm.getLocation()));
182        ext.addExtension("message", new StringType(vm.getMessage()));
183      }
184    }
185    if (message.getServer() != null) {
186      issue.addExtension(ExtensionDefinitions.EXT_ISSUE_SERVER, new UrlType(message.getServer()));
187    }
188    return issue;
189  }
190
191  public static OperationOutcomeIssueComponent convertToIssueSimpleWithId(ValidationMessage message, OperationOutcome op) {
192    OperationOutcomeIssueComponent issue = convertToIssueSimple(message, op);
193    if (message.getMessageId() != null) {
194      ExtensionUtilities.setStringExtension(issue, ExtensionDefinitions.EXT_ISSUE_MSG_ID, message.getMessageId());
195    }
196    return issue;
197  }
198
199  private static String errorSummaryForSlicingAsText(List<ValidationMessage> list) {
200    StringBuilder b = new StringBuilder();
201    for (ValidationMessage vm : list) {
202      if (vm.isSlicingHint()) {
203        if (vm.hasSliceInfo()) {
204          for (ValidationMessage s : vm.getSliceInfo()) {
205            b.append(vm.getLocation() + ": " + s);
206          }
207        } else {
208          b.append(vm.getLocation() + ": " + vm.getMessage());
209        }
210      } else if (vm.getLevel() == org.hl7.fhir.utilities.validation.ValidationMessage.IssueSeverity.ERROR || vm.getLevel() == org.hl7.fhir.utilities.validation.ValidationMessage.IssueSeverity.FATAL) {
211        b.append(vm.getLocation() + ": " + vm.getHtml());
212      }
213    }
214    return b.toString();
215  }
216
217  public static OperationOutcome createOutcomeSimple(List<ValidationMessage> messages) {
218    OperationOutcome res = new OperationOutcome();
219    for (ValidationMessage vm : messages) {
220      res.addIssue(convertToIssueSimple(vm, res));
221    }
222    return res;
223  }
224
225  public static OperationOutcome createOutcomeSimpleWithIds(List<ValidationMessage> messages) {
226    OperationOutcome res = new OperationOutcome();
227    for (ValidationMessage vm : messages) {
228      res.addIssue(convertToIssueSimpleWithId(vm, res));
229    }
230    return res;
231  }
232
233  public static OperationOutcome outcomeFromTextError(String text) {
234    OperationOutcome oo = new OperationOutcome();
235    oo.getText().setStatus(NarrativeStatus.GENERATED);
236    oo.getText().setDiv(new XhtmlNode(NodeType.Element, "div"));
237    oo.getText().getDiv().tx(text);
238    OperationOutcomeIssueComponent issue = oo.addIssue();
239    issue.setSeverity(IssueSeverity.ERROR);
240    issue.setCode(IssueType.EXCEPTION);
241    issue.getDetails().setText(text);
242    return oo;
243  }
244
245}