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 org.apache.commons.lang3.StringUtils;
028import org.apache.commons.lang3.builder.EqualsBuilder;
029import org.apache.commons.lang3.builder.HashCodeBuilder;
030import org.apache.commons.lang3.builder.ToStringBuilder;
031import org.apache.commons.lang3.builder.ToStringStyle;
032import org.hl7.fhir.instance.model.api.IBaseCoding;
033
034import static org.apache.commons.lang3.StringUtils.defaultString;
035import static org.apache.commons.lang3.StringUtils.isNotBlank;
036
037public class TokenParam extends BaseParam /*implements IQueryParameterType*/ {
038
039        private TokenParamModifier myModifier;
040        private String mySystem;
041        private String myValue;
042
043        private Boolean myMdmExpand;
044
045        /**
046         * Constructor
047         */
048        public TokenParam() {
049                super();
050        }
051
052        /**
053         * Constructor which copies the {@link InternalCodingDt#getSystemElement() system} and
054         * {@link InternalCodingDt#getCodeElement() code} from a {@link InternalCodingDt} instance and adds it as a parameter
055         *
056         * @param theCodingDt The coding
057         */
058        public TokenParam(BaseCodingDt theCodingDt) {
059                this(
060                                toSystemValue(theCodingDt.getSystemElement()),
061                                theCodingDt.getCodeElement().getValue());
062        }
063
064        /**
065         * Constructor which copies the {@link BaseIdentifierDt#getSystemElement() system} and
066         * {@link BaseIdentifierDt#getValueElement() value} from a {@link BaseIdentifierDt} instance and adds it as a
067         * parameter
068         *
069         * @param theIdentifierDt The identifier
070         */
071        public TokenParam(BaseIdentifierDt theIdentifierDt) {
072                this(
073                                toSystemValue(theIdentifierDt.getSystemElement()),
074                                theIdentifierDt.getValueElement().getValue());
075        }
076
077        /**
078         * Construct a {@link TokenParam} from the {@link IBaseCoding#getSystem()} () system} and
079         * {@link IBaseCoding#getCode()} () code} of a {@link IBaseCoding} instance.
080         *
081         * @param theCoding The coding
082         */
083        public TokenParam(IBaseCoding theCoding) {
084                this(theCoding.getSystem(), theCoding.getCode());
085        }
086
087        public TokenParam(String theSystem, String theValue) {
088                setSystem(theSystem);
089                setValue(theValue);
090        }
091
092        public TokenParam(String theSystem, String theValue, boolean theText) {
093                if (theText && isNotBlank(theSystem)) {
094                        throw new IllegalArgumentException(
095                                        Msg.code(1938)
096                                                        + "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");
097                }
098                setSystem(theSystem);
099                setValue(theValue);
100                setText(theText);
101        }
102
103        /**
104         * Constructor that takes a code but no system
105         */
106        public TokenParam(String theCode) {
107                this(null, theCode);
108        }
109
110        public boolean isMdmExpand() {
111                return myMdmExpand != null && myMdmExpand;
112        }
113
114        public TokenParam setMdmExpand(boolean theMdmExpand) {
115                myMdmExpand = theMdmExpand;
116                return this;
117        }
118
119        @Override
120        String doGetQueryParameterQualifier() {
121                if (getModifier() != null) {
122                        return getModifier().getValue();
123                }
124                return null;
125        }
126
127        /**
128         * {@inheritDoc}
129         */
130        @Override
131        String doGetValueAsQueryToken(FhirContext theContext) {
132                if (getSystem() != null) {
133                        if (getValue() != null) {
134                                return ParameterUtil.escape(StringUtils.defaultString(getSystem()))
135                                                + '|'
136                                                + ParameterUtil.escape(getValue());
137                        } else {
138                                return ParameterUtil.escape(StringUtils.defaultString(getSystem())) + '|';
139                        }
140                }
141                return ParameterUtil.escape(getValue());
142        }
143
144        /**
145         * {@inheritDoc}
146         */
147        @Override
148        void doSetValueAsQueryToken(FhirContext theContext, String theParamName, String theQualifier, String theParameter) {
149                setModifier(null);
150                if (theQualifier != null) {
151                        TokenParamModifier modifier = TokenParamModifier.forValue(theQualifier);
152                        setModifier(modifier);
153
154                        if (modifier == TokenParamModifier.TEXT) {
155                                setSystem(null);
156                                setValue(ParameterUtil.unescape(theParameter));
157                                return;
158                        }
159                }
160
161                setSystem(null);
162                if (theParameter == null) {
163                        setValue(null);
164                } else {
165                        int barIndex = ParameterUtil.nonEscapedIndexOf(theParameter, '|');
166                        if (barIndex != -1) {
167                                setSystem(theParameter.substring(0, barIndex));
168                                setValue(ParameterUtil.unescape(theParameter.substring(barIndex + 1)));
169                        } else {
170                                setValue(ParameterUtil.unescape(theParameter));
171                        }
172                }
173        }
174
175        /**
176         * Returns the modifier for this token
177         */
178        public TokenParamModifier getModifier() {
179                return myModifier;
180        }
181
182        public TokenParam setModifier(TokenParamModifier theModifier) {
183                myModifier = theModifier;
184                return this;
185        }
186
187        /**
188         * Returns the system for this token. Note that if a {@link #getModifier()} is being used, the entire value of the
189         * parameter will be placed in {@link #getValue() value} and this method will return <code>null</code>.
190         * <p
191         * Also note that this value may be <code>null</code> or <code>""</code> (empty string) and that
192         * each of these have a different meaning. When a token is passed on a URL and it has no
193         * vertical bar (often meaning "return values that match the given code in any codesystem")
194         * this method will return <code>null</code>. When a token is passed on a URL and it has
195         * a vetical bar but nothing before the bar (often meaning "return values that match the
196         * given code but that have no codesystem) this method will return <code>""</code>
197         * </p>
198         */
199        public String getSystem() {
200                return mySystem;
201        }
202
203        public TokenParam setSystem(String theSystem) {
204                mySystem = theSystem;
205                return this;
206        }
207
208        /**
209         * Returns the value for the token (generally the value to the right of the
210         * vertical bar on the URL)
211         */
212        public String getValue() {
213                return myValue;
214        }
215
216        public TokenParam setValue(String theValue) {
217                myValue = theValue;
218                return this;
219        }
220
221        public InternalCodingDt getValueAsCoding() {
222                return new InternalCodingDt(mySystem, myValue);
223        }
224
225        public String getValueNotNull() {
226                return defaultString(myValue);
227        }
228
229        public boolean isEmpty() {
230                return StringUtils.isBlank(mySystem) && StringUtils.isBlank(myValue) && getMissing() == null;
231        }
232
233        /**
234         * Returns true if {@link #getModifier()} returns {@link TokenParamModifier#TEXT}
235         */
236        public boolean isText() {
237                return myModifier == TokenParamModifier.TEXT;
238        }
239
240        /**
241         * @deprecated Use {@link #setModifier(TokenParamModifier)} instead
242         */
243        @Deprecated
244        public TokenParam setText(boolean theText) {
245                if (theText) {
246                        myModifier = TokenParamModifier.TEXT;
247                } else {
248                        myModifier = null;
249                }
250                return this;
251        }
252
253        @Override
254        public String toString() {
255                ToStringBuilder builder = new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE);
256                builder.append("system", defaultString(getSystem()));
257                if (myModifier != null) {
258                        builder.append(":" + myModifier.getValue());
259                }
260                builder.append("value", getValue());
261                if (getMissing() != null) {
262                        builder.append(":missing", getMissing());
263                }
264                return builder.toString();
265        }
266
267        @Override
268        public boolean equals(Object theO) {
269                if (this == theO) {
270                        return true;
271                }
272
273                if (theO == null || getClass() != theO.getClass()) {
274                        return false;
275                }
276
277                TokenParam that = (TokenParam) theO;
278
279                EqualsBuilder b = new EqualsBuilder();
280                b.append(myModifier, that.myModifier);
281                b.append(mySystem, that.mySystem);
282                b.append(myValue, that.myValue);
283                return b.isEquals();
284        }
285
286        @Override
287        public int hashCode() {
288                HashCodeBuilder b = new HashCodeBuilder(17, 37);
289                b.append(myModifier);
290                b.append(mySystem);
291                b.append(myValue);
292                return b.toHashCode();
293        }
294
295        private static String toSystemValue(UriDt theSystem) {
296                return theSystem.getValueAsString();
297        }
298}