001/*-
002 * #%L
003 * HAPI FHIR - Core Library
004 * %%
005 * Copyright (C) 2014 - 2025 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.i18n.Msg;
023import ca.uhn.fhir.parser.DataFormatException;
024
025import java.util.Objects;
026
027import static org.apache.commons.lang3.StringUtils.isBlank;
028
029public abstract class BaseParamWithPrefix<T extends BaseParam> extends BaseParam {
030
031        private static final long serialVersionUID = 1L;
032        private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(BaseParamWithPrefix.class);
033
034        public static final String MSG_PREFIX_INVALID_FORMAT = "Invalid date/time/quantity format: ";
035
036        private ParamPrefixEnum myPrefix;
037
038        /**
039         * Constructor
040         */
041        // Default since this is internal
042        BaseParamWithPrefix() {
043                super();
044        }
045
046        /**
047         * Eg. if this is invoked with "gt2012-11-02", sets the prefix to GREATER_THAN and returns "2012-11-02"
048         */
049        String extractPrefixAndReturnRest(String theString) {
050                int offset = 0;
051                while (true) {
052                        if (theString.length() == offset) {
053                                break;
054                        } else {
055                                char nextChar = theString.charAt(offset);
056                                if (nextChar == '-' || nextChar == '%' || Character.isDigit(nextChar)) {
057                                        break;
058                                }
059                        }
060                        offset++;
061                }
062
063                if (offset > 0 && theString.length() == offset) {
064                        throw new DataFormatException(Msg.code(1940) + MSG_PREFIX_INVALID_FORMAT + "\"" + theString + "\"");
065                }
066
067                String prefix = theString.substring(0, offset);
068                if (!isBlank(prefix)) {
069
070                        myPrefix = ParamPrefixEnum.forValue(prefix);
071
072                        if (myPrefix == null) {
073                                // prefix doesn't match standard values.  Try legacy values
074                                switch (prefix) {
075                                        case ">=":
076                                                myPrefix = ParamPrefixEnum.GREATERTHAN_OR_EQUALS;
077                                                break;
078                                        case ">":
079                                                myPrefix = ParamPrefixEnum.GREATERTHAN;
080                                                break;
081                                        case "<=":
082                                                myPrefix = ParamPrefixEnum.LESSTHAN_OR_EQUALS;
083                                                break;
084                                        case "<":
085                                                myPrefix = ParamPrefixEnum.LESSTHAN;
086                                                break;
087                                        case "~":
088                                                myPrefix = ParamPrefixEnum.APPROXIMATE;
089                                                break;
090                                        case "=":
091                                                myPrefix = ParamPrefixEnum.EQUAL;
092                                                break;
093                                        default:
094                                                throw new DataFormatException(Msg.code(1941) + "Invalid prefix: \"" + prefix + "\"");
095                                }
096                                ourLog.warn(
097                                                "Date parameter has legacy prefix '{}' which has been removed from FHIR. This should be replaced with '{}'",
098                                                prefix,
099                                                myPrefix.getValue());
100                        }
101                }
102
103                return theString.substring(offset);
104        }
105
106        /**
107         * Returns the prefix used by this parameter (e.g. "<code>gt</code>", or "<code>eq</code>")
108         */
109        public ParamPrefixEnum getPrefix() {
110                return myPrefix;
111        }
112
113        /**
114         * Sets the prefix used by this parameter (e.g. "<code>gt</code>", or "<code>eq</code>")
115         */
116        @SuppressWarnings("unchecked")
117        public T setPrefix(ParamPrefixEnum thePrefix) {
118                myPrefix = thePrefix;
119                return (T) this;
120        }
121
122        @Override
123        public boolean equals(Object theO) {
124                if (!(theO instanceof BaseParamWithPrefix<?> that)) {
125                        return false;
126                }
127                return super.equals(theO) && this.myPrefix == that.myPrefix;
128        }
129
130        @Override
131        public int hashCode() {
132                return Objects.hash(this.myPrefix, super.hashCode());
133        }
134}