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}