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}