001package ca.uhn.fhir.rest.param;
002
003/*
004 * #%L
005 * HAPI FHIR - Core Library
006 * %%
007 * Copyright (C) 2014 - 2021 Smile CDR, Inc.
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 */
022
023import ca.uhn.fhir.context.FhirContext;
024import ca.uhn.fhir.model.primitive.IdDt;
025import ca.uhn.fhir.rest.api.Constants;
026import ca.uhn.fhir.util.CoverageIgnore;
027import org.apache.commons.lang3.builder.ToStringBuilder;
028import org.apache.commons.lang3.builder.ToStringStyle;
029import org.hl7.fhir.instance.model.api.IBaseResource;
030import org.hl7.fhir.instance.model.api.IIdType;
031
032import java.math.BigDecimal;
033
034import static ca.uhn.fhir.model.primitive.IdDt.isValidLong;
035import static org.apache.commons.lang3.StringUtils.isBlank;
036import static org.apache.commons.lang3.StringUtils.isNotBlank;
037
038public class ReferenceParam extends BaseParam /*implements IQueryParameterType*/ {
039
040        private String myChain;
041        private String myResourceType;
042        private String myBaseUrl;
043        private String myValue;
044        private String myIdPart;
045        private Boolean myMdmExpand;
046
047        /**
048         * Constructor
049         */
050        public ReferenceParam() {
051                super();
052        }
053
054        /**
055         * Constructor
056         */
057        public ReferenceParam(String theValue) {
058                setValueAsQueryToken(null, null, null, theValue);
059        }
060
061        /**
062         * Constructor
063         */
064        public ReferenceParam(String theChain, String theValue) {
065                setValueAsQueryToken(null, null, null, theValue);
066                setChain(theChain);
067        }
068
069        /**
070         * Constructor
071         */
072        public ReferenceParam(String theResourceType, String theChain, String theValue) {
073                String qualifier = "";
074                if (isNotBlank(theResourceType)) {
075                        qualifier = ":" + theResourceType;
076                }
077                if (isNotBlank(theChain)) {
078                        qualifier = qualifier + "." + theChain;
079                }
080
081                setValueAsQueryToken(null, null, qualifier, theValue);
082        }
083
084        /**
085         * Constructor
086         *
087         * @since 5.0.0
088         */
089        public ReferenceParam(IIdType theValue) {
090                if (theValue != null) {
091                        setValueAsQueryToken(null, null, null, theValue.getValue());
092                }
093        }
094
095
096        private String defaultGetQueryParameterQualifier() {
097                StringBuilder b = new StringBuilder();
098                if (isNotBlank(myChain)) {
099                        if (isNotBlank(getResourceType())) {
100                                b.append(':');
101                                b.append(getResourceType());
102                        }
103                        b.append('.');
104                        b.append(myChain);
105                }
106                if (b.length() != 0) {
107                        return b.toString();
108                }
109                return null;
110        }
111        @Override
112        String doGetQueryParameterQualifier() {
113                return this.myMdmExpand != null ? ":mdm" : defaultGetQueryParameterQualifier();
114        }
115
116        @Override
117        String doGetValueAsQueryToken(FhirContext theContext) {
118                if (isBlank(getResourceType())) {
119                        return myValue; // e.g. urn:asdjd or 123 or cid:wieiuru or #1
120                } else {
121                        if (isBlank(getChain()) && isNotBlank(getResourceType())) {
122                                return getResourceType() + "/" + getIdPart();
123                        }
124                        return myValue;
125                }
126        }
127
128        @Override
129        void doSetValueAsQueryToken(FhirContext theContext, String theParamName, String theQualifier, String theValue) {
130                if (Constants.PARAMQUALIFIER_MDM.equals(theQualifier)) {
131                        myMdmExpand = true;
132                        theQualifier = "";
133                }
134
135                String q = theQualifier;
136                if (isNotBlank(q)) {
137                        if (q.startsWith(":")) {
138                                int nextIdx = q.indexOf('.');
139                                if (nextIdx != -1) {
140                                        myChain = q.substring(nextIdx + 1);
141                                        myResourceType = q.substring(1, nextIdx);
142                                } else {
143                                        myChain = null;
144                                        myResourceType = q.substring(1);
145                                }
146
147                                myValue = theValue;
148                                myIdPart = theValue;
149
150                                IdDt id = new IdDt(theValue);
151                                if (!id.hasBaseUrl() && id.hasIdPart() && id.hasResourceType()) {
152                                        if (id.getResourceType().equals(myResourceType)) {
153                                                myIdPart = id.getIdPart();
154                                        }
155                                }
156
157                        } else if (q.startsWith(".")) {
158                                myChain = q.substring(1);
159                                myResourceType = null;
160                                myValue = theValue;
161                                myIdPart = theValue;
162                        }
163                } else {
164                        myChain = null;
165                        myValue = theValue;
166                        IdDt id = new IdDt(theValue);
167                        myResourceType = id.getResourceType();
168                        myIdPart = id.getIdPart();
169                        myBaseUrl = id.getBaseUrl();
170                }
171
172        }
173
174
175        @CoverageIgnore
176        public String getBaseUrl() {
177                return myBaseUrl;
178        }
179
180        public boolean isMdmExpand() {
181                return myMdmExpand != null && myMdmExpand;
182        }
183
184        public ReferenceParam setMdmExpand(boolean theMdmExpand) {
185                myMdmExpand = theMdmExpand;
186                return this;
187        }
188
189        public String getChain() {
190                return myChain;
191        }
192
193        public ReferenceParam setChain(String theChain) {
194                myChain = theChain;
195                return this;
196        }
197
198        @CoverageIgnore
199        public String getIdPart() {
200                return myIdPart;
201        }
202
203        @CoverageIgnore
204        public BigDecimal getIdPartAsBigDecimal() {
205                return new IdDt(myValue).getIdPartAsBigDecimal();
206        }
207
208        @CoverageIgnore
209        public Long getIdPartAsLong() {
210                return new IdDt(myValue).getIdPartAsLong();
211        }
212
213        public String getResourceType() {
214                if (isNotBlank(myResourceType)) {
215                        return myResourceType;
216                }
217                if (isBlank(myChain)) {
218                        return new IdDt(myValue).getResourceType();
219                }
220                return null;
221        }
222
223        public Class<? extends IBaseResource> getResourceType(FhirContext theCtx) {
224                if (isBlank(getResourceType())) {
225                        return null;
226                }
227                return theCtx.getResourceDefinition(getResourceType()).getImplementingClass();
228        }
229
230        public String getValue() {
231                return myValue;
232        }
233
234        /**
235         * Note that the parameter to this method <b>must</b> be a resource reference, e.g
236         * <code>123</code> or <code>Patient/123</code> or <code>http://example.com/fhir/Patient/123</code>
237         * or something like this. This is not appropriate for cases where a chain is being used and
238         * the value is for a different type of parameter (e.g. a token). In that case, use one of the
239         * setter constructors.
240         */
241        public ReferenceParam setValue(String theValue) {
242                IdDt id = new IdDt(theValue);
243                String qualifier = null;
244                if (id.hasResourceType()) {
245                        qualifier = ":" + id.getResourceType();
246                }
247                setValueAsQueryToken(null, null, qualifier, id.getIdPart());
248                return this;
249        }
250
251        public boolean hasResourceType() {
252                return isNotBlank(myResourceType);
253        }
254
255        @Override
256        protected boolean isSupportsChain() {
257                return true;
258        }
259
260        /**
261         * Returns a new param containing the same value as this param, but with the type copnverted
262         * to {@link DateParam}. This is useful if you are using reference parameters and want to handle
263         * chained parameters of different types in a single method.
264         * <p>
265         * See <a href="https://hapifhir.io/hapi-fhir/docs/server_plain/rest_operations_search.html#chained-resource-references">Dynamic Chains</a>
266         * in the HAPI FHIR documentation for an example of how to use this method.
267         * </p>
268         */
269        public DateParam toDateParam(FhirContext theContext) {
270                DateParam retVal = new DateParam();
271                retVal.setValueAsQueryToken(theContext, null, null, getValueAsQueryToken(theContext));
272                return retVal;
273        }
274
275        /**
276         * Returns a new param containing the same value as this param, but with the type copnverted
277         * to {@link NumberParam}. This is useful if you are using reference parameters and want to handle
278         * chained parameters of different types in a single method.
279         * <p>
280         * See <a href="https://hapifhir.io/hapi-fhir/docs/server_plain/rest_operations_search.html#chained-resource-references">Dynamic Chains</a>
281         * in the HAPI FHIR documentation for an example of how to use this method.
282         * </p>
283         */
284        public NumberParam toNumberParam(FhirContext theContext) {
285                NumberParam retVal = new NumberParam();
286                retVal.setValueAsQueryToken(theContext, null, null, getValueAsQueryToken(theContext));
287                return retVal;
288        }
289
290        /**
291         * Returns a new param containing the same value as this param, but with the type copnverted
292         * to {@link QuantityParam}. This is useful if you are using reference parameters and want to handle
293         * chained parameters of different types in a single method.
294         * <p>
295         * See <a href="https://hapifhir.io/hapi-fhir/docs/server_plain/rest_operations_search.html#chained-resource-references">Dynamic Chains</a>
296         * in the HAPI FHIR documentation for an example of how to use this method.
297         * </p>
298         */
299        public QuantityParam toQuantityParam(FhirContext theContext) {
300                QuantityParam retVal = new QuantityParam();
301                retVal.setValueAsQueryToken(theContext, null, null, getValueAsQueryToken(theContext));
302                return retVal;
303        }
304
305        @Override
306        public String toString() {
307                ToStringBuilder b = new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE);
308                if (isNotBlank(myChain)) {
309                        b.append("chain", myChain);
310                }
311                b.append("value", getValue());
312                return b.build();
313        }
314
315        /**
316         * Returns a new param containing the same value as this param, but with the type copnverted
317         * to {@link StringParam}. This is useful if you are using reference parameters and want to handle
318         * chained parameters of different types in a single method.
319         * <p>
320         * See <a href="https://hapifhir.io/hapi-fhir/docs/server_plain/rest_operations_search.html#chained-resource-references">Dynamic Chains</a>
321         * in the HAPI FHIR documentation for an example of how to use this method.
322         * </p>
323         */
324        public StringParam toStringParam(FhirContext theContext) {
325                StringParam retVal = new StringParam();
326                retVal.setValueAsQueryToken(theContext, null, null, getValueAsQueryToken(theContext));
327                return retVal;
328        }
329
330        /**
331         * Returns a new param containing the same value as this param, but with the type copnverted
332         * to {@link TokenParam}. This is useful if you are using reference parameters and want to handle
333         * chained parameters of different types in a single method.
334         * <p>
335         * See <a href="https://hapifhir.io/hapi-fhir/docs/server_plain/rest_operations_search.html#chained-resource-references">Dynamic Chains</a>
336         * in the HAPI FHIR documentation for an example of how to use this method.
337         * </p>
338         */
339        public TokenParam toTokenParam(FhirContext theContext) {
340                TokenParam retVal = new TokenParam();
341                retVal.setValueAsQueryToken(theContext, null, null, getValueAsQueryToken(theContext));
342                return retVal;
343        }
344
345        public boolean isIdPartValidLong() {
346                return isValidLong(getIdPart());
347        }
348}