001package ca.uhn.fhir.rest.param;
002
003/*
004 * #%L
005 * HAPI FHIR - Core Library
006 * %%
007 * Copyright (C) 2014 - 2018 University Health Network
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.api.IQueryParameterOr;
025import ca.uhn.fhir.model.api.TemporalPrecisionEnum;
026import ca.uhn.fhir.model.api.annotation.SimpleSetter;
027import ca.uhn.fhir.model.primitive.BaseDateTimeDt;
028import ca.uhn.fhir.model.primitive.DateDt;
029import ca.uhn.fhir.model.primitive.DateTimeDt;
030import ca.uhn.fhir.model.primitive.InstantDt;
031import ca.uhn.fhir.parser.DataFormatException;
032import ca.uhn.fhir.rest.api.QualifiedParamList;
033import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
034import ca.uhn.fhir.util.ObjectUtil;
035import ca.uhn.fhir.util.ValidateUtil;
036import org.apache.commons.lang3.ObjectUtils;
037import org.apache.commons.lang3.builder.ToStringBuilder;
038import org.apache.commons.lang3.builder.ToStringStyle;
039import org.apache.commons.lang3.time.DateUtils;
040import org.hl7.fhir.instance.model.api.IPrimitiveType;
041
042import java.util.*;
043
044import static org.apache.commons.lang3.StringUtils.isNotBlank;
045
046public class DateParam extends BaseParamWithPrefix<DateParam> implements /*IQueryParameterType , */IQueryParameterOr<DateParam> {
047
048        private static final long serialVersionUID = 1L;
049        
050        private final DateParamDateTimeHolder myValue = new DateParamDateTimeHolder();
051
052        /**
053         * Constructor
054         */
055        public DateParam() {
056        }
057
058        /**
059         * Constructor
060         */
061        public DateParam(ParamPrefixEnum thePrefix, Date theDate) {
062                setPrefix(thePrefix);
063                setValue(theDate);
064        }
065
066        /**
067         * Constructor
068         */
069        public DateParam(ParamPrefixEnum thePrefix, DateTimeDt theDate) {
070                setPrefix(thePrefix);
071                myValue.setValueAsString(theDate != null ? theDate.getValueAsString() : null);
072        }
073
074        /**
075         * Constructor
076         */
077        public DateParam(ParamPrefixEnum thePrefix, IPrimitiveType<Date> theDate) {
078                setPrefix(thePrefix);
079                myValue.setValueAsString(theDate != null ? theDate.getValueAsString() : null);
080        }
081
082        /**
083         * Constructor
084         */
085        public DateParam(ParamPrefixEnum thePrefix, long theDate) {
086                ValidateUtil.isGreaterThan(theDate, 0, "theDate must not be 0 or negative");
087                setPrefix(thePrefix);
088                setValue(new Date(theDate));
089        }
090
091        /**
092         * Constructor
093         */
094        public DateParam(ParamPrefixEnum thePrefix, String theDate) {
095                setPrefix(thePrefix);
096                setValueAsString(theDate);
097        }
098
099
100        /**
101         * Constructor which takes a complete [qualifier]{date} string.
102         * 
103         * @param theString
104         *           The string
105         */
106        public DateParam(String theString) {
107                setValueAsQueryToken(null, null, null, theString);
108        }
109
110        @Override
111        String doGetQueryParameterQualifier() {
112                return null;
113        }
114
115        @Override
116        String doGetValueAsQueryToken(FhirContext theContext) {
117                StringBuilder b = new StringBuilder();
118                if (getPrefix() != null) {
119                        b.append(ParameterUtil.escapeWithDefault(getPrefix().getValue()));
120                }
121                
122                if (myValue != null) {
123                        b.append(ParameterUtil.escapeWithDefault(myValue.getValueAsString()));
124                }
125
126                return b.toString();
127        }
128
129        @Override
130        void doSetValueAsQueryToken(FhirContext theContext, String theParamName, String theQualifier, String theValue) {
131                setValueAsString(theValue);
132        }
133
134        public TemporalPrecisionEnum getPrecision() {
135                if (myValue != null) {
136                        return myValue.getPrecision();
137                }
138                return null;
139        }
140
141        public Date getValue() {
142                if (myValue != null) {
143                        return myValue.getValue();
144                }
145                return null;
146        }
147
148        public DateTimeDt getValueAsDateTimeDt() {
149                if (myValue == null) {
150                        return null;
151                }
152                return new DateTimeDt(myValue.getValue());
153        }
154
155        public InstantDt getValueAsInstantDt() {
156                if (myValue == null) {
157                        return null;
158                }
159                return new InstantDt(myValue.getValue());
160        }
161
162        public String getValueAsString() {
163                if (myValue != null) {
164                        return myValue.getValueAsString();
165                }
166                return null;
167        }
168
169        @Override
170        public List<DateParam> getValuesAsQueryTokens() {
171                return Collections.singletonList(this);
172        }
173
174        /**
175         * Returns <code>true</code> if no date/time is specified. Note that this method does not check the comparator, so a
176         * QualifiedDateParam with only a comparator and no date/time is considered empty.
177         */
178        public boolean isEmpty() {
179                return myValue.isEmpty();
180        }
181
182        /**
183         * Sets the value of the param to the given date (sets to the {@link TemporalPrecisionEnum#MILLI millisecond}
184         * precision, and will be encoded using the system local time zone).
185         */
186        public DateParam setValue(Date theValue) {
187                myValue.setValue(theValue, TemporalPrecisionEnum.MILLI);
188                return this;
189        }
190
191        /**
192         * Sets the value using a FHIR Date type, such as a {@link DateDt}, or a DateTimeType.
193         */
194        public void setValue(IPrimitiveType<Date> theValue) {
195                if (theValue != null) {
196                        myValue.setValueAsString(theValue.getValueAsString());
197                } else {
198                        myValue.setValue(null);
199                }
200        }
201
202        /**
203         * Accepts values with or without a prefix (e.g. <code>gt2011-01-01</code> and <code>2011-01-01</code>).
204         * If no prefix is provided in the given value, the {@link #getPrefix() existing prefix} is preserved
205         */
206        public void setValueAsString(String theDate) {
207                if (isNotBlank(theDate)) {
208                        ParamPrefixEnum existingPrefix = getPrefix();
209                        myValue.setValueAsString(super.extractPrefixAndReturnRest(theDate));
210                        if (getPrefix() == null) {
211                                setPrefix(existingPrefix);
212                        }
213                } else {
214                        myValue.setValue(null);
215                }
216        }
217
218        @Override
219        public void  setValuesAsQueryTokens(FhirContext theContext, String theParamName, QualifiedParamList theParameters) {
220                setMissing(null);
221                setPrefix(null);
222                setValueAsString(null);
223                
224                if (theParameters.size() == 1) {
225                        setValueAsString(theParameters.get(0));
226                } else if (theParameters.size() > 1) {
227                        throw new InvalidRequestException("This server does not support multi-valued dates for this parameter: " + theParameters);
228                }
229                
230        }
231
232        @Override
233        public boolean equals(Object obj) {
234                if (obj == this) {
235                        return true;
236                }
237                if (!(obj instanceof DateParam)) {
238                        return false;
239                }
240                DateParam other = (DateParam) obj;
241                return  Objects.equals(getValue(), other.getValue()) &&
242                                        Objects.equals(getPrefix(), other.getPrefix());
243        }
244
245        @Override
246        public int hashCode() {
247                return Objects.hash(getValue(), getPrefix());
248        }
249
250        @Override
251        public String toString() {
252                ToStringBuilder b = new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE);
253                b.append("prefix", getPrefix());
254                b.append("value", getValueAsString());
255                return b.build();
256        }
257
258        public class DateParamDateTimeHolder extends BaseDateTimeDt {
259                @Override
260                protected TemporalPrecisionEnum getDefaultPrecisionForDatatype() {
261                        return TemporalPrecisionEnum.SECOND;
262                }
263
264                @Override
265                protected boolean isPrecisionAllowed(TemporalPrecisionEnum thePrecision) {
266                        return true;
267                }
268        }
269}