001/*
002 * #%L
003 * HAPI FHIR - Core Library
004 * %%
005 * Copyright (C) 2014 - 2024 Smile CDR, Inc.
006 * %%
007 * Licensed under the Apache License, Version 2.0 (the "License");
008 * you may not use this file except in compliance with the License.
009 * You may obtain a copy of the License at
010 *
011 *      http://www.apache.org/licenses/LICENSE-2.0
012 *
013 * Unless required by applicable law or agreed to in writing, software
014 * distributed under the License is distributed on an "AS IS" BASIS,
015 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
016 * See the License for the specific language governing permissions and
017 * limitations under the License.
018 * #L%
019 */
020package ca.uhn.fhir.rest.param;
021
022import ca.uhn.fhir.context.FhirContext;
023import ca.uhn.fhir.i18n.Msg;
024import ca.uhn.fhir.model.base.composite.BaseCodingDt;
025import ca.uhn.fhir.model.base.composite.BaseIdentifierDt;
026import ca.uhn.fhir.model.primitive.UriDt;
027import ca.uhn.fhir.rest.api.Constants;
028import org.apache.commons.lang3.StringUtils;
029import org.apache.commons.lang3.builder.EqualsBuilder;
030import org.apache.commons.lang3.builder.HashCodeBuilder;
031import org.apache.commons.lang3.builder.ToStringBuilder;
032import org.apache.commons.lang3.builder.ToStringStyle;
033import org.hl7.fhir.instance.model.api.IBaseCoding;
034
035import static org.apache.commons.lang3.StringUtils.defaultString;
036import static org.apache.commons.lang3.StringUtils.isNotBlank;
037
038public class TokenParam extends BaseParam /*implements IQueryParameterType*/ {
039
040        private TokenParamModifier myModifier;
041        private String mySystem;
042        private String myValue;
043
044        private Boolean myMdmExpand;
045
046        /**
047         * Constructor
048         */
049        public TokenParam() {
050                super();
051        }
052
053        /**
054         * Constructor which copies the {@link InternalCodingDt#getSystemElement() system} and
055         * {@link InternalCodingDt#getCodeElement() code} from a {@link InternalCodingDt} instance and adds it as a parameter
056         *
057         * @param theCodingDt The coding
058         */
059        public TokenParam(BaseCodingDt theCodingDt) {
060                this(
061                                toSystemValue(theCodingDt.getSystemElement()),
062                                theCodingDt.getCodeElement().getValue());
063        }
064
065        /**
066         * Constructor which copies the {@link BaseIdentifierDt#getSystemElement() system} and
067         * {@link BaseIdentifierDt#getValueElement() value} from a {@link BaseIdentifierDt} instance and adds it as a
068         * parameter
069         *
070         * @param theIdentifierDt The identifier
071         */
072        public TokenParam(BaseIdentifierDt theIdentifierDt) {
073                this(
074                                toSystemValue(theIdentifierDt.getSystemElement()),
075                                theIdentifierDt.getValueElement().getValue());
076        }
077
078        /**
079         * Construct a {@link TokenParam} from the {@link IBaseCoding#getSystem()} () system} and
080         * {@link IBaseCoding#getCode()} () code} of a {@link IBaseCoding} instance.
081         *
082         * @param theCoding The coding
083         */
084        public TokenParam(IBaseCoding theCoding) {
085                this(theCoding.getSystem(), theCoding.getCode());
086        }
087
088        public TokenParam(String theSystem, String theValue) {
089                setSystem(theSystem);
090                setValue(theValue);
091        }
092
093        public TokenParam(String theSystem, String theValue, boolean theText) {
094                if (theText && isNotBlank(theSystem)) {
095                        throw new IllegalArgumentException(
096                                        Msg.code(1938)
097                                                        + "theSystem can not be non-blank if theText is true (:text searches do not include a system). In other words, set the first parameter to null for a text search");
098                }
099                setSystem(theSystem);
100                setValue(theValue);
101                setText(theText);
102        }
103
104        /**
105         * Constructor that takes a code but no system
106         */
107        public TokenParam(String theCode) {
108                this(null, theCode);
109        }
110
111        public boolean isMdmExpand() {
112                return myMdmExpand != null && myMdmExpand;
113        }
114
115        public TokenParam setMdmExpand(boolean theMdmExpand) {
116                myMdmExpand = theMdmExpand;
117                return this;
118        }
119
120        @Override
121        String doGetQueryParameterQualifier() {
122                if (getModifier() != null) {
123                        return getModifier().getValue();
124                }
125                return null;
126        }
127
128        /**
129         * {@inheritDoc}
130         */
131        @Override
132        String doGetValueAsQueryToken(FhirContext theContext) {
133                if (getSystem() != null) {
134                        if (getValue() != null) {
135                                return ParameterUtil.escape(StringUtils.defaultString(getSystem()))
136                                                + '|'
137                                                + ParameterUtil.escape(getValue());
138                        } else {
139                                return ParameterUtil.escape(StringUtils.defaultString(getSystem())) + '|';
140                        }
141                }
142                return ParameterUtil.escape(getValue());
143        }
144
145        /**
146         * {@inheritDoc}
147         */
148        @Override
149        void doSetValueAsQueryToken(FhirContext theContext, String theParamName, String theQualifier, String theParameter) {
150                setModifier(null);
151                setSystem(null);
152
153                if (theQualifier != null) {
154                        if (Constants.PARAMQUALIFIER_MDM.equals(theQualifier)) {
155                                setMdmExpand(true);
156                        }
157
158                        TokenParamModifier modifier = TokenParamModifier.forValue(theQualifier);
159                        setModifier(modifier);
160
161                        if (modifier == TokenParamModifier.TEXT) {
162                                setValue(ParameterUtil.unescape(theParameter));
163                                return;
164                        }
165                }
166
167                if (theParameter == null) {
168                        setValue(null);
169                } else {
170                        int barIndex = ParameterUtil.nonEscapedIndexOf(theParameter, '|');
171                        if (barIndex != -1) {
172                                setSystem(theParameter.substring(0, barIndex));
173                                setValue(ParameterUtil.unescape(theParameter.substring(barIndex + 1)));
174                        } else {
175                                setValue(ParameterUtil.unescape(theParameter));
176                        }
177                }
178        }
179
180        /**
181         * Returns the modifier for this token
182         */
183        public TokenParamModifier getModifier() {
184                return myModifier;
185        }
186
187        public TokenParam setModifier(TokenParamModifier theModifier) {
188                myModifier = theModifier;
189                return this;
190        }
191
192        /**
193         * Returns the system for this token. Note that if a {@link #getModifier()} is being used, the entire value of the
194         * parameter will be placed in {@link #getValue() value} and this method will return <code>null</code>.
195         * <p
196         * Also note that this value may be <code>null</code> or <code>""</code> (empty string) and that
197         * each of these have a different meaning. When a token is passed on a URL and it has no
198         * vertical bar (often meaning "return values that match the given code in any codesystem")
199         * this method will return <code>null</code>. When a token is passed on a URL and it has
200         * a vetical bar but nothing before the bar (often meaning "return values that match the
201         * given code but that have no codesystem) this method will return <code>""</code>
202         * </p>
203         */
204        public String getSystem() {
205                return mySystem;
206        }
207
208        public TokenParam setSystem(String theSystem) {
209                mySystem = theSystem;
210                return this;
211        }
212
213        /**
214         * Returns the value for the token (generally the value to the right of the
215         * vertical bar on the URL)
216         */
217        public String getValue() {
218                return myValue;
219        }
220
221        public TokenParam setValue(String theValue) {
222                myValue = theValue;
223                return this;
224        }
225
226        public InternalCodingDt getValueAsCoding() {
227                return new InternalCodingDt(mySystem, myValue);
228        }
229
230        public String getValueNotNull() {
231                return defaultString(myValue);
232        }
233
234        public boolean isEmpty() {
235                return StringUtils.isBlank(mySystem) && StringUtils.isBlank(myValue) && getMissing() == null;
236        }
237
238        /**
239         * Returns true if {@link #getModifier()} returns {@link TokenParamModifier#TEXT}
240         */
241        public boolean isText() {
242                return myModifier == TokenParamModifier.TEXT;
243        }
244
245        /**
246         * @deprecated Use {@link #setModifier(TokenParamModifier)} instead
247         */
248        @Deprecated
249        public TokenParam setText(boolean theText) {
250                if (theText) {
251                        myModifier = TokenParamModifier.TEXT;
252                } else {
253                        myModifier = null;
254                }
255                return this;
256        }
257
258        @Override
259        public String toString() {
260                ToStringBuilder builder = new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE);
261                builder.append("system", defaultString(getSystem()));
262                if (myModifier != null) {
263                        builder.append(":" + myModifier.getValue());
264                }
265                builder.append("value", getValue());
266                if (getMissing() != null) {
267                        builder.append(":missing", getMissing());
268                }
269                return builder.toString();
270        }
271
272        @Override
273        public boolean equals(Object theO) {
274                if (this == theO) {
275                        return true;
276                }
277
278                if (theO == null || getClass() != theO.getClass()) {
279                        return false;
280                }
281
282                TokenParam that = (TokenParam) theO;
283
284                EqualsBuilder b = new EqualsBuilder();
285                b.append(myModifier, that.myModifier);
286                b.append(mySystem, that.mySystem);
287                b.append(myValue, that.myValue);
288                return b.isEquals();
289        }
290
291        @Override
292        public int hashCode() {
293                HashCodeBuilder b = new HashCodeBuilder(17, 37);
294                b.append(myModifier);
295                b.append(mySystem);
296                b.append(myValue);
297                return b.toHashCode();
298        }
299
300        private static String toSystemValue(UriDt theSystem) {
301                return theSystem.getValueAsString();
302        }
303}