001package ca.uhn.fhir.util;
002
003/*
004 * #%L
005 * HAPI FHIR - Core Library
006 * %%
007 * Copyright (C) 2014 - 2016 University Health Network
008 * %%
009 * Licensed under the Apache License, Version 2.0 (the "License");
010 * you may not use this file except in compliance with the License.
011 * You may obtain a copy of the License at
012 * 
013 *      http://www.apache.org/licenses/LICENSE-2.0
014 * 
015 * Unless required by applicable law or agreed to in writing, software
016 * distributed under the License is distributed on an "AS IS" BASIS,
017 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
018 * See the License for the specific language governing permissions and
019 * limitations under the License.
020 * #L%
021 */
022import static org.apache.commons.lang3.StringUtils.isNotBlank;
023
024import java.util.List;
025
026import org.hl7.fhir.instance.model.api.IBase;
027import org.hl7.fhir.instance.model.api.IBaseOperationOutcome;
028import org.hl7.fhir.instance.model.api.IBaseResource;
029import org.hl7.fhir.instance.model.api.IPrimitiveType;
030
031import ca.uhn.fhir.context.BaseRuntimeChildDefinition;
032import ca.uhn.fhir.context.BaseRuntimeElementCompositeDefinition;
033import ca.uhn.fhir.context.BaseRuntimeElementDefinition;
034import ca.uhn.fhir.context.FhirContext;
035import ca.uhn.fhir.context.FhirVersionEnum;
036import ca.uhn.fhir.context.RuntimeResourceDefinition;
037import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
038
039/**
040 * Utilities for dealing with OperationOutcome resources across various model versions
041 */
042public class OperationOutcomeUtil {
043
044//      /**
045//       * Add an issue to an OperationOutcome
046//       * 
047//       * @param theCtx
048//       *           The fhir context
049//       * @param theOperationOutcome
050//       *           The OO resource to add to
051//       * @param theSeverity
052//       *           The severity (e.g. "error")
053//       * @param theDetails
054//       *           The details string
055//       * @param theCode 
056//       */
057//      public static void addIssue(FhirContext theCtx, IBaseOperationOutcome theOperationOutcome, String theSeverity, String theDetails, String theCode) {
058//              IBase issue = createIssue(theCtx, theOperationOutcome);
059//              populateDetails(theCtx, issue, theSeverity, theDetails, null, theCode);
060//      }
061
062        /**
063         * Add an issue to an OperationOutcome
064         * 
065         * @param theCtx
066         *           The fhir context
067         * @param theOperationOutcome
068         *           The OO resource to add to
069         * @param theSeverity
070         *           The severity (e.g. "error")
071         * @param theDetails
072         *           The details string
073         * @param theCode 
074         */
075        public static void addIssue(FhirContext theCtx, IBaseOperationOutcome theOperationOutcome, String theSeverity, String theDetails, String theLocation, String theCode) {
076                IBase issue = createIssue(theCtx, theOperationOutcome);
077                populateDetails(theCtx, issue, theSeverity, theDetails, theLocation, theCode);
078        }
079
080        private static IBase createIssue(FhirContext theCtx, IBaseResource theOutcome) {
081                RuntimeResourceDefinition ooDef = theCtx.getResourceDefinition(theOutcome);
082                BaseRuntimeChildDefinition issueChild = ooDef.getChildByName("issue");
083                BaseRuntimeElementCompositeDefinition<?> issueElement = (BaseRuntimeElementCompositeDefinition<?>) issueChild.getChildByName("issue");
084
085                IBase issue = issueElement.newInstance();
086                issueChild.getMutator().addValue(theOutcome, issue);
087                return issue;
088        }
089
090        public static String getFirstIssueDetails(FhirContext theCtx, IBaseOperationOutcome theOutcome) {
091                if (theCtx.getVersion().getVersion().isNewerThan(FhirVersionEnum.DSTU1)) {
092                        return getFirstIssueStringPart(theCtx, theOutcome, "diagnostics");
093                } else {
094                        return getFirstIssueStringPart(theCtx, theOutcome, "details");
095                }
096        }
097
098        public static String getFirstIssueLocation(FhirContext theCtx, IBaseOperationOutcome theOutcome) {
099                return getFirstIssueStringPart(theCtx, theOutcome, "location");
100        }
101
102        private static String getFirstIssueStringPart(FhirContext theCtx, IBaseOperationOutcome theOutcome, String name) {
103                if (theOutcome == null) {
104                        return null;
105                }
106
107                RuntimeResourceDefinition ooDef = theCtx.getResourceDefinition(theOutcome);
108                BaseRuntimeChildDefinition issueChild = ooDef.getChildByName("issue");
109
110                List<IBase> issues = issueChild.getAccessor().getValues(theOutcome);
111                if (issues.isEmpty()) {
112                        return null;
113                }
114
115                IBase issue = issues.get(0);
116                BaseRuntimeElementCompositeDefinition<?> issueElement = (BaseRuntimeElementCompositeDefinition<?>) theCtx.getElementDefinition(issue.getClass());
117                BaseRuntimeChildDefinition detailsChild = issueElement.getChildByName(name);
118
119                List<IBase> details = detailsChild.getAccessor().getValues(issue);
120                if (details.isEmpty()) {
121                        return null;
122                }
123                return ((IPrimitiveType<?>) details.get(0)).getValueAsString();
124        }
125
126        /**
127         * Returns true if the given OperationOutcome has 1 or more Operation.issue repetitions
128         */
129        public static boolean hasIssues(FhirContext theCtx, IBaseOperationOutcome theOutcome) {
130                if (theOutcome == null) {
131                        return false;
132                }
133                RuntimeResourceDefinition ooDef = theCtx.getResourceDefinition(theOutcome);
134                BaseRuntimeChildDefinition issueChild = ooDef.getChildByName("issue");
135                return issueChild.getAccessor().getValues(theOutcome).size() > 0;
136        }
137
138        public static IBaseOperationOutcome newInstance(FhirContext theCtx) {
139                RuntimeResourceDefinition ooDef = theCtx.getResourceDefinition("OperationOutcome");
140                try {
141                        return (IBaseOperationOutcome) ooDef.getImplementingClass().newInstance();
142                } catch (InstantiationException e) {
143                        throw new InternalErrorException("Unable to instantiate OperationOutcome", e);
144                } catch (IllegalAccessException e) {
145                        throw new InternalErrorException("Unable to instantiate OperationOutcome", e);
146                }
147        }
148
149        private static void populateDetails(FhirContext theCtx, IBase theIssue, String theSeverity, String theDetails, String theLocation, String theCode) {
150                BaseRuntimeElementCompositeDefinition<?> issueElement = (BaseRuntimeElementCompositeDefinition<?>) theCtx.getElementDefinition(theIssue.getClass());
151                BaseRuntimeChildDefinition detailsChild;
152                if (theCtx.getVersion().getVersion().isNewerThan(FhirVersionEnum.DSTU1)) {
153                        detailsChild = issueElement.getChildByName("diagnostics");
154                        
155                        BaseRuntimeChildDefinition codeChild = issueElement.getChildByName("code");
156                        IPrimitiveType<?> codeElem = (IPrimitiveType<?>) codeChild.getChildByName("code").newInstance(codeChild.getInstanceConstructorArguments());
157                        codeElem.setValueAsString(theCode);
158                        codeChild.getMutator().addValue(theIssue, codeElem);
159                } else {
160                        detailsChild = issueElement.getChildByName("details");
161                }
162                
163                BaseRuntimeElementDefinition<?> stringDef = detailsChild.getChildByName(detailsChild.getElementName());
164                BaseRuntimeChildDefinition severityChild = issueElement.getChildByName("severity");
165                BaseRuntimeChildDefinition locationChild = issueElement.getChildByName("location");
166
167                IPrimitiveType<?> severityElem = (IPrimitiveType<?>) severityChild.getChildByName("severity").newInstance(severityChild.getInstanceConstructorArguments());
168                severityElem.setValueAsString(theSeverity);
169                severityChild.getMutator().addValue(theIssue, severityElem);
170
171                IPrimitiveType<?> string = (IPrimitiveType<?>) stringDef.newInstance();
172                string.setValueAsString(theDetails);
173                detailsChild.getMutator().setValue(theIssue, string);
174
175                if (isNotBlank(theLocation)) {
176                        IPrimitiveType<?> locationElem = (IPrimitiveType<?>) locationChild.getChildByName("location").newInstance(locationChild.getInstanceConstructorArguments());
177                        locationElem.setValueAsString(theLocation);
178                        locationChild.getMutator().addValue(theIssue, locationElem);
179                }
180        }
181
182}