001package ca.uhn.fhir.validation; 002 003import ca.uhn.fhir.context.FhirContext; 004import ca.uhn.fhir.i18n.Msg; 005import ca.uhn.fhir.parser.IParser; 006import ca.uhn.fhir.parser.LenientErrorHandler; 007import ca.uhn.fhir.rest.api.EncodingEnum; 008import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; 009import ca.uhn.fhir.util.ObjectUtil; 010import org.hl7.fhir.instance.model.api.IBaseResource; 011 012import javax.annotation.Nonnull; 013import java.util.ArrayList; 014import java.util.List; 015 016import static org.apache.commons.lang3.ObjectUtils.defaultIfNull; 017 018/* 019 * #%L 020 * HAPI FHIR - Core Library 021 * %% 022 * Copyright (C) 2014 - 2022 Smile CDR, Inc. 023 * %% 024 * Licensed under the Apache License, Version 2.0 (the "License"); 025 * you may not use this file except in compliance with the License. 026 * You may obtain a copy of the License at 027 * 028 * http://www.apache.org/licenses/LICENSE-2.0 029 * 030 * Unless required by applicable law or agreed to in writing, software 031 * distributed under the License is distributed on an "AS IS" BASIS, 032 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 033 * See the License for the specific language governing permissions and 034 * limitations under the License. 035 * #L% 036 */ 037 038public class ValidationContext<T> extends BaseValidationContext<T> implements IValidationContext<T> { 039 040 private final IEncoder myEncoder; 041 private final T myResource; 042 private final EncodingEnum myResourceAsStringEncoding; 043 private final ValidationOptions myOptions; 044 private String myResourceAsString; 045 046 private ValidationContext(FhirContext theContext, T theResource, IEncoder theEncoder, ValidationOptions theOptions) { 047 this(theContext, theResource, theEncoder, new ArrayList<>(), theOptions); 048 } 049 050 private ValidationContext(FhirContext theContext, T theResource, IEncoder theEncoder, List<SingleValidationMessage> theMessages, ValidationOptions theOptions) { 051 super(theContext, theMessages); 052 myResource = theResource; 053 myEncoder = theEncoder; 054 myOptions = theOptions; 055 if (theEncoder != null) { 056 myResourceAsStringEncoding = theEncoder.getEncoding(); 057 } else { 058 myResourceAsStringEncoding = null; 059 } 060 } 061 062 @Override 063 public T getResource() { 064 return myResource; 065 } 066 067 @Override 068 public String getResourceAsString() { 069 if (myResourceAsString == null) { 070 myResourceAsString = myEncoder.encode(); 071 } 072 return myResourceAsString; 073 } 074 075 @Override 076 public EncodingEnum getResourceAsStringEncoding() { 077 return myResourceAsStringEncoding; 078 } 079 080 @Nonnull 081 @Override 082 public ValidationOptions getOptions() { 083 return myOptions; 084 } 085 086 private interface IEncoder { 087 String encode(); 088 089 EncodingEnum getEncoding(); 090 } 091 092 public static <T extends IBaseResource> IValidationContext<T> forResource(final FhirContext theContext, final T theResource, ValidationOptions theOptions) { 093 ObjectUtil.requireNonNull(theContext, "theContext can not be null"); 094 ObjectUtil.requireNonNull(theResource, "theResource can not be null"); 095 ValidationOptions options = defaultIfNull(theOptions, ValidationOptions.empty()); 096 097 IEncoder encoder = new IEncoder() { 098 @Override 099 public String encode() { 100 return theContext.newJsonParser().encodeResourceToString(theResource); 101 } 102 103 @Override 104 public EncodingEnum getEncoding() { 105 return EncodingEnum.JSON; 106 } 107 }; 108 return new ValidationContext<>(theContext, theResource, encoder, options); 109 } 110 111 public static IValidationContext<IBaseResource> forText(final FhirContext theContext, final String theResourceBody, final ValidationOptions theOptions) { 112 ObjectUtil.requireNonNull(theContext, "theContext can not be null"); 113 ObjectUtil.requireNotEmpty(theResourceBody, "theResourceBody can not be null or empty"); 114 ValidationOptions options = defaultIfNull(theOptions, ValidationOptions.empty()); 115 116 return new BaseValidationContext<IBaseResource>(theContext) { 117 118 private EncodingEnum myEncoding; 119 private IBaseResource myParsed; 120 121 @Override 122 public IBaseResource getResource() { 123 if (myParsed == null) { 124 IParser parser = getResourceAsStringEncoding().newParser(getFhirContext()); 125 LenientErrorHandler errorHandler = new LenientErrorHandler(); 126 errorHandler.setErrorOnInvalidValue(false); 127 parser.setParserErrorHandler(errorHandler); 128 myParsed = parser.parseResource(getResourceAsString()); 129 } 130 return myParsed; 131 } 132 133 @Override 134 public String getResourceAsString() { 135 return theResourceBody; 136 } 137 138 @Override 139 public EncodingEnum getResourceAsStringEncoding() { 140 if (myEncoding == null) { 141 myEncoding = EncodingEnum.detectEncodingNoDefault(theResourceBody); 142 if (myEncoding == null) { 143 throw new InvalidRequestException(Msg.code(1971) + theContext.getLocalizer().getMessage(ValidationContext.class, "unableToDetermineEncoding")); 144 } 145 } 146 return myEncoding; 147 } 148 149 @Nonnull 150 @Override 151 public ValidationOptions getOptions() { 152 return options; 153 } 154 155 }; 156 } 157 158 public static IValidationContext<IBaseResource> subContext(final IValidationContext<IBaseResource> theCtx, final IBaseResource theResource, ValidationOptions theOptions) { 159 return new ValidationContext<>(theCtx.getFhirContext(), theResource, new IEncoder() { 160 @Override 161 public String encode() { 162 return theCtx.getFhirContext().newXmlParser().encodeResourceToString(theResource); 163 } 164 165 @Override 166 public EncodingEnum getEncoding() { 167 return EncodingEnum.XML; 168 } 169 }, theCtx.getMessages(), theOptions); 170 } 171}