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