
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}