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