
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}