001package ca.uhn.fhir.rest.param;
002
003import ca.uhn.fhir.context.FhirContext;
004import ca.uhn.fhir.model.api.IQueryParameterAnd;
005import ca.uhn.fhir.model.api.TemporalPrecisionEnum;
006import ca.uhn.fhir.parser.DataFormatException;
007import ca.uhn.fhir.rest.api.QualifiedParamList;
008import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
009import org.apache.commons.lang3.time.DateUtils;
010import org.hl7.fhir.instance.model.api.IPrimitiveType;
011
012import java.util.*;
013
014import static ca.uhn.fhir.rest.param.ParamPrefixEnum.*;
015import static java.lang.String.format;
016import static org.apache.commons.lang3.StringUtils.isNotBlank;
017
018/*
019 * #%L
020 * HAPI FHIR - Core Library
021 * %%
022 * Copyright (C) 2014 - 2020 University Health Network
023 * %%
024 * Licensed under the Apache License, Version 2.0 (the "License");
025 * you may not use this file except in compliance with the License.
026 * You may obtain a copy of the License at
027 *
028 *      http://www.apache.org/licenses/LICENSE-2.0
029 *
030 * Unless required by applicable law or agreed to in writing, software
031 * distributed under the License is distributed on an "AS IS" BASIS,
032 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
033 * See the License for the specific language governing permissions and
034 * limitations under the License.
035 * #L%
036 */
037
038@SuppressWarnings("UnusedReturnValue")
039public class DateRangeParam implements IQueryParameterAnd<DateParam> {
040
041        private static final long serialVersionUID = 1L;
042
043        private DateParam myLowerBound;
044        private DateParam myUpperBound;
045
046        /**
047         * Basic constructor. Values must be supplied by calling {@link #setLowerBound(DateParam)} and
048         * {@link #setUpperBound(DateParam)}
049         */
050        public DateRangeParam() {
051                super();
052        }
053
054        /**
055         * Constructor which takes two Dates representing the lower and upper bounds of the range (inclusive on both ends)
056         *
057         * @param theLowerBound A qualified date param representing the lower date bound (optionally may include time), e.g.
058         *                      "2011-02-22" or "2011-02-22T13:12:00Z". Will be treated inclusively. Either theLowerBound or
059         *                      theUpperBound may both be populated, or one may be null, but it is not valid for both to be null.
060         * @param theUpperBound A qualified date param representing the upper date bound (optionally may include time), e.g.
061         *                      "2011-02-22" or "2011-02-22T13:12:00Z". Will be treated inclusively. Either theLowerBound or
062         *                      theUpperBound may both be populated, or one may be null, but it is not valid for both to be null.
063         */
064        public DateRangeParam(Date theLowerBound, Date theUpperBound) {
065                this();
066                setRangeFromDatesInclusive(theLowerBound, theUpperBound);
067        }
068
069        /**
070         * Sets the range from a single date param. If theDateParam has no qualifier, treats it as the lower and upper bound
071         * (e.g. 2011-01-02 would match any time on that day). If theDateParam has a qualifier, treats it as either the lower
072         * or upper bound, with no opposite bound.
073         */
074        public DateRangeParam(DateParam theDateParam) {
075                this();
076                if (theDateParam == null) {
077                        throw new NullPointerException("theDateParam can not be null");
078                }
079                if (theDateParam.isEmpty()) {
080                        throw new IllegalArgumentException("theDateParam can not be empty");
081                }
082                if (theDateParam.getPrefix() == null) {
083                        setRangeFromDatesInclusive(theDateParam.getValueAsString(), theDateParam.getValueAsString());
084                } else {
085                        switch (theDateParam.getPrefix()) {
086                                case EQUAL:
087                                        setRangeFromDatesInclusive(theDateParam.getValueAsString(), theDateParam.getValueAsString());
088                                        break;
089                                case STARTS_AFTER:
090                                case GREATERTHAN:
091                                case GREATERTHAN_OR_EQUALS:
092                                        validateAndSet(theDateParam, null);
093                                        break;
094                                case ENDS_BEFORE:
095                                case LESSTHAN:
096                                case LESSTHAN_OR_EQUALS:
097                                        validateAndSet(null, theDateParam);
098                                        break;
099                                default:
100                                        // Should not happen
101                                        throw new InvalidRequestException("Invalid comparator for date range parameter:" + theDateParam.getPrefix() + ". This is a bug.");
102                        }
103                }
104        }
105
106        /**
107         * Constructor which takes two Dates representing the lower and upper bounds of the range (inclusive on both ends)
108         *
109         * @param theLowerBound A qualified date param representing the lower date bound (optionally may include time), e.g.
110         *                      "2011-02-22" or "2011-02-22T13:12:00Z". Will be treated inclusively. Either theLowerBound or
111         *                      theUpperBound may both be populated, or one may be null, but it is not valid for both to be null.
112         * @param theUpperBound A qualified date param representing the upper date bound (optionally may include time), e.g.
113         *                      "2011-02-22" or "2011-02-22T13:12:00Z". Will be treated inclusively. Either theLowerBound or
114         *                      theUpperBound may both be populated, or one may be null, but it is not valid for both to be null.
115         */
116        public DateRangeParam(DateParam theLowerBound, DateParam theUpperBound) {
117                this();
118                setRangeFromDatesInclusive(theLowerBound, theUpperBound);
119        }
120
121        /**
122         * Constructor which takes two Dates representing the lower and upper bounds of the range (inclusive on both ends)
123         *
124         * @param theLowerBound A qualified date param representing the lower date bound (optionally may include time), e.g.
125         *                      "2011-02-22" or "2011-02-22T13:12:00Z". Will be treated inclusively. Either theLowerBound or
126         *                      theUpperBound may both be populated, or one may be null, but it is not valid for both to be null.
127         * @param theUpperBound A qualified date param representing the upper date bound (optionally may include time), e.g.
128         *                      "2011-02-22" or "2011-02-22T13:12:00Z". Will be treated inclusively. Either theLowerBound or
129         *                      theUpperBound may both be populated, or one may be null, but it is not valid for both to be null.
130         */
131        public DateRangeParam(IPrimitiveType<Date> theLowerBound, IPrimitiveType<Date> theUpperBound) {
132                this();
133                setRangeFromDatesInclusive(theLowerBound, theUpperBound);
134        }
135
136        /**
137         * Constructor which takes two strings representing the lower and upper bounds of the range (inclusive on both ends)
138         *
139         * @param theLowerBound An unqualified date param representing the lower date bound (optionally may include time), e.g.
140         *                      "2011-02-22" or "2011-02-22T13:12:00Z". Either theLowerBound or theUpperBound may both be populated, or
141         *                      one may be null, but it is not valid for both to be null.
142         * @param theUpperBound An unqualified date param representing the upper date bound (optionally may include time), e.g.
143         *                      "2011-02-22" or "2011-02-22T13:12:00Z". Either theLowerBound or theUpperBound may both be populated, or
144         *                      one may be null, but it is not valid for both to be null.
145         */
146        public DateRangeParam(String theLowerBound, String theUpperBound) {
147                this();
148                setRangeFromDatesInclusive(theLowerBound, theUpperBound);
149        }
150
151        private void addParam(DateParam theParsed) throws InvalidRequestException {
152                if (theParsed.getPrefix() == null || theParsed.getPrefix() == EQUAL) {
153                        if (myLowerBound != null || myUpperBound != null) {
154                                throw new InvalidRequestException("Can not have multiple date range parameters for the same param without a qualifier");
155                        }
156
157                        if (theParsed.getMissing() != null) {
158                                myLowerBound = theParsed;
159                                myUpperBound = theParsed;
160                        } else {
161                                myLowerBound = new DateParam(EQUAL, theParsed.getValueAsString());
162                                myUpperBound = new DateParam(EQUAL, theParsed.getValueAsString());
163                        }
164
165                } else {
166
167                        switch (theParsed.getPrefix()) {
168                                case GREATERTHAN:
169                                case GREATERTHAN_OR_EQUALS:
170                                        if (myLowerBound != null) {
171                                                throw new InvalidRequestException("Can not have multiple date range parameters for the same param that specify a lower bound");
172                                        }
173                                        myLowerBound = theParsed;
174                                        break;
175                                case LESSTHAN:
176                                case LESSTHAN_OR_EQUALS:
177                                        if (myUpperBound != null) {
178                                                throw new InvalidRequestException("Can not have multiple date range parameters for the same param that specify an upper bound");
179                                        }
180                                        myUpperBound = theParsed;
181                                        break;
182                                default:
183                                        throw new InvalidRequestException("Unknown comparator: " + theParsed.getPrefix());
184                        }
185
186                }
187        }
188
189        @Override
190        public boolean equals(Object obj) {
191                if (obj == this) {
192                        return true;
193                }
194                if (!(obj instanceof DateRangeParam)) {
195                        return false;
196                }
197                DateRangeParam other = (DateRangeParam) obj;
198                return Objects.equals(myLowerBound, other.myLowerBound) &&
199                        Objects.equals(myUpperBound, other.myUpperBound);
200        }
201
202        public DateParam getLowerBound() {
203                return myLowerBound;
204        }
205
206        public DateRangeParam setLowerBound(DateParam theLowerBound) {
207                validateAndSet(theLowerBound, myUpperBound);
208                return this;
209        }
210
211        /**
212         * Sets the lower bound using a string that is compliant with
213         * FHIR dateTime format (ISO-8601).
214         * <p>
215         * This lower bound is assumed to have a <code>ge</code>
216         * (greater than or equals) modifier.
217         * </p>
218         * <p>
219         * Note: An operation can take a DateRangeParam. If only a single date is provided,
220         * it will still result in a DateRangeParam where the lower and upper bounds
221         * are the same value. As such, even though the prefixes for the lower and
222         * upper bounds default to <code>ge</code> and <code>le</code> respectively,
223         * the resulting prefix is effectively <code>eq</code> where only a single
224         * date is provided - as required by the FHIR specificiation (i.e. "If no
225         * prefix is present, the prefix <code>eq</code> is assumed").
226         * </p>
227         */
228        public DateRangeParam setLowerBound(String theLowerBound) {
229                setLowerBound(new DateParam(GREATERTHAN_OR_EQUALS, theLowerBound));
230                return this;
231        }
232
233        /**
234         * Sets the lower bound to be greaterthan or equal to the given date
235         */
236        public DateRangeParam setLowerBoundInclusive(Date theLowerBound) {
237                validateAndSet(new DateParam(ParamPrefixEnum.GREATERTHAN_OR_EQUALS, theLowerBound), myUpperBound);
238                return this;
239        }
240
241        /**
242         * Sets the upper bound to be greaterthan or equal to the given date
243         */
244        public DateRangeParam setUpperBoundInclusive(Date theUpperBound) {
245                validateAndSet(myLowerBound, new DateParam(ParamPrefixEnum.LESSTHAN_OR_EQUALS, theUpperBound));
246                return this;
247        }
248
249
250        /**
251         * Sets the lower bound to be greaterthan to the given date
252         */
253        public DateRangeParam setLowerBoundExclusive(Date theLowerBound) {
254                validateAndSet(new DateParam(ParamPrefixEnum.GREATERTHAN, theLowerBound), myUpperBound);
255                return this;
256        }
257
258        /**
259         * Sets the upper bound to be greaterthan to the given date
260         */
261        public DateRangeParam setUpperBoundExclusive(Date theUpperBound) {
262                validateAndSet(myLowerBound, new DateParam(ParamPrefixEnum.LESSTHAN, theUpperBound));
263                return this;
264        }
265
266        public Date getLowerBoundAsInstant() {
267                if (myLowerBound == null || myLowerBound.getValue() == null) {
268                        return null;
269                }
270                Date retVal = myLowerBound.getValue();
271
272                if (myLowerBound.getPrecision().ordinal() <= TemporalPrecisionEnum.DAY.ordinal()) {
273                        Calendar cal = DateUtils.toCalendar(retVal);
274                        cal.setTimeZone(TimeZone.getTimeZone("GMT-11:30"));
275                        cal = DateUtils.truncate(cal, Calendar.DATE);
276                        retVal = cal.getTime();
277                }
278
279                if (myLowerBound.getPrefix() != null) {
280                        switch (myLowerBound.getPrefix()) {
281                                case GREATERTHAN:
282                                case STARTS_AFTER:
283                                        retVal = myLowerBound.getPrecision().add(retVal, 1);
284                                        break;
285                                case EQUAL:
286                                case GREATERTHAN_OR_EQUALS:
287                                        break;
288                                case LESSTHAN:
289                                case APPROXIMATE:
290                                case LESSTHAN_OR_EQUALS:
291                                case ENDS_BEFORE:
292                                case NOT_EQUAL:
293                                        throw new IllegalStateException("Invalid lower bound comparator: " + myLowerBound.getPrefix());
294                        }
295                }
296                return retVal;
297        }
298
299        public DateParam getUpperBound() {
300                return myUpperBound;
301        }
302
303        /**
304         * Sets the upper bound using a string that is compliant with
305         * FHIR dateTime format (ISO-8601).
306         * <p>
307         * This upper bound is assumed to have a <code>le</code>
308         * (less than or equals) modifier.
309         * </p>
310         * <p>
311         * Note: An operation can take a DateRangeParam. If only a single date is provided,
312         * it will still result in a DateRangeParam where the lower and upper bounds
313         * are the same value. As such, even though the prefixes for the lower and
314         * upper bounds default to <code>ge</code> and <code>le</code> respectively,
315         * the resulting prefix is effectively <code>eq</code> where only a single
316         * date is provided - as required by the FHIR specificiation (i.e. "If no
317         * prefix is present, the prefix <code>eq</code> is assumed").
318         * </p>
319         */
320        public DateRangeParam setUpperBound(String theUpperBound) {
321                setUpperBound(new DateParam(LESSTHAN_OR_EQUALS, theUpperBound));
322                return this;
323        }
324
325        public DateRangeParam setUpperBound(DateParam theUpperBound) {
326                validateAndSet(myLowerBound, theUpperBound);
327                return this;
328        }
329
330        public Date getUpperBoundAsInstant() {
331                if (myUpperBound == null || myUpperBound.getValue() == null) {
332                        return null;
333                }
334
335                Date retVal = myUpperBound.getValue();
336
337                if (myUpperBound.getPrecision().ordinal() <= TemporalPrecisionEnum.DAY.ordinal()) {
338                        Calendar cal = DateUtils.toCalendar(retVal);
339                        cal.setTimeZone(TimeZone.getTimeZone("GMT+11:30"));
340                        cal = DateUtils.truncate(cal, Calendar.DATE);
341                        retVal = cal.getTime();
342                }
343
344                if (myUpperBound.getPrefix() != null) {
345                        switch (myUpperBound.getPrefix()) {
346                                case LESSTHAN:
347                                case ENDS_BEFORE:
348                                        retVal = new Date(retVal.getTime() - 1L);
349                                        break;
350                                case EQUAL:
351                                case LESSTHAN_OR_EQUALS:
352                                        retVal = myUpperBound.getPrecision().add(retVal, 1);
353                                        retVal = new Date(retVal.getTime() - 1L);
354                                        break;
355                                case GREATERTHAN_OR_EQUALS:
356                                case GREATERTHAN:
357                                case APPROXIMATE:
358                                case NOT_EQUAL:
359                                case STARTS_AFTER:
360                                        throw new IllegalStateException("Invalid upper bound comparator: " + myUpperBound.getPrefix());
361                        }
362                }
363                return retVal;
364        }
365
366        @Override
367        public List<DateParam> getValuesAsQueryTokens() {
368                ArrayList<DateParam> retVal = new ArrayList<>();
369                if (myLowerBound != null && myLowerBound.getMissing() != null) {
370                        retVal.add((myLowerBound));
371                } else {
372                        if (myLowerBound != null && !myLowerBound.isEmpty()) {
373                                retVal.add((myLowerBound));
374                        }
375                        if (myUpperBound != null && !myUpperBound.isEmpty()) {
376                                retVal.add((myUpperBound));
377                        }
378                }
379                return retVal;
380        }
381
382        private boolean hasBound(DateParam bound) {
383                return bound != null && !bound.isEmpty();
384        }
385
386        @Override
387        public int hashCode() {
388                return Objects.hash(myLowerBound, myUpperBound);
389        }
390
391        public boolean isEmpty() {
392                return (getLowerBoundAsInstant() == null) && (getUpperBoundAsInstant() == null);
393        }
394
395        /**
396         * Sets the range from a pair of dates, inclusive on both ends
397         *
398         * @param theLowerBound A qualified date param representing the lower date bound (optionally may include time), e.g.
399         *                      "2011-02-22" or "2011-02-22T13:12:00Z". Will be treated inclusively. Either theLowerBound or
400         *                      theUpperBound may both be populated, or one may be null, but it is not valid for both to be null.
401         * @param theUpperBound A qualified date param representing the upper date bound (optionally may include time), e.g.
402         *                      "2011-02-22" or "2011-02-22T13:12:00Z". Will be treated inclusively. Either theLowerBound or
403         *                      theUpperBound may both be populated, or one may be null, but it is not valid for both to be null.
404         */
405        public void setRangeFromDatesInclusive(Date theLowerBound, Date theUpperBound) {
406                DateParam lowerBound = theLowerBound != null
407                        ? new DateParam(GREATERTHAN_OR_EQUALS, theLowerBound) : null;
408                DateParam upperBound = theUpperBound != null
409                        ? new DateParam(LESSTHAN_OR_EQUALS, theUpperBound) : null;
410                validateAndSet(lowerBound, upperBound);
411        }
412
413        /**
414         * Sets the range from a pair of dates, inclusive on both ends
415         *
416         * @param theLowerBound A qualified date param representing the lower date bound (optionally may include time), e.g.
417         *                      "2011-02-22" or "2011-02-22T13:12:00Z". Will be treated inclusively. Either theLowerBound or
418         *                      theUpperBound may both be populated, or one may be null, but it is not valid for both to be null.
419         * @param theUpperBound A qualified date param representing the upper date bound (optionally may include time), e.g.
420         *                      "2011-02-22" or "2011-02-22T13:12:00Z". Will be treated inclusively. Either theLowerBound or
421         *                      theUpperBound may both be populated, or one may be null, but it is not valid for both to be null.
422         */
423        public void setRangeFromDatesInclusive(DateParam theLowerBound, DateParam theUpperBound) {
424                validateAndSet(theLowerBound, theUpperBound);
425        }
426
427        /**
428         * Sets the range from a pair of dates, inclusive on both ends. Note that if
429         * theLowerBound is after theUpperBound, thie method will automatically reverse
430         * the order of the arguments in order to create an inclusive range.
431         *
432         * @param theLowerBound A qualified date param representing the lower date bound (optionally may include time), e.g.
433         *                      "2011-02-22" or "2011-02-22T13:12:00Z". Will be treated inclusively. Either theLowerBound or
434         *                      theUpperBound may both be populated, or one may be null, but it is not valid for both to be null.
435         * @param theUpperBound A qualified date param representing the upper date bound (optionally may include time), e.g.
436         *                      "2011-02-22" or "2011-02-22T13:12:00Z". Will be treated inclusively. Either theLowerBound or
437         *                      theUpperBound may both be populated, or one may be null, but it is not valid for both to be null.
438         */
439        public void setRangeFromDatesInclusive(IPrimitiveType<Date> theLowerBound, IPrimitiveType<Date> theUpperBound) {
440                IPrimitiveType<Date> lowerBound = theLowerBound;
441                IPrimitiveType<Date> upperBound = theUpperBound;
442                if (lowerBound != null && lowerBound.getValue() != null && upperBound != null && upperBound.getValue() != null) {
443                        if (lowerBound.getValue().after(upperBound.getValue())) {
444                                IPrimitiveType<Date> temp = lowerBound;
445                                lowerBound = upperBound;
446                                upperBound = temp;
447                        }
448                }
449                validateAndSet(
450                        lowerBound != null ? new DateParam(GREATERTHAN_OR_EQUALS, lowerBound) : null,
451                        upperBound != null ? new DateParam(LESSTHAN_OR_EQUALS, upperBound) : null);
452        }
453
454        /**
455         * Sets the range from a pair of dates, inclusive on both ends
456         *
457         * @param theLowerBound A qualified date param representing the lower date bound (optionally may include time), e.g.
458         *                      "2011-02-22" or "2011-02-22T13:12:00Z". Will be treated inclusively. Either theLowerBound or
459         *                      theUpperBound may both be populated, or one may be null, but it is not valid for both to be null.
460         * @param theUpperBound A qualified date param representing the upper date bound (optionally may include time), e.g.
461         *                      "2011-02-22" or "2011-02-22T13:12:00Z". Will be treated inclusively. Either theLowerBound or
462         *                      theUpperBound may both be populated, or one may be null, but it is not valid for both to be null.
463         */
464        public void setRangeFromDatesInclusive(String theLowerBound, String theUpperBound) {
465                DateParam lowerBound = theLowerBound != null
466                        ? new DateParam(GREATERTHAN_OR_EQUALS, theLowerBound)
467                        : null;
468                DateParam upperBound = theUpperBound != null
469                        ? new DateParam(LESSTHAN_OR_EQUALS, theUpperBound)
470                        : null;
471                if (isNotBlank(theLowerBound) && isNotBlank(theUpperBound) && theLowerBound.equals(theUpperBound)) {
472                        lowerBound.setPrefix(EQUAL);
473                        upperBound.setPrefix(EQUAL);
474                }
475                validateAndSet(lowerBound, upperBound);
476        }
477
478        @Override
479        public void setValuesAsQueryTokens(FhirContext theContext, String theParamName, List<QualifiedParamList> theParameters)
480                throws InvalidRequestException {
481
482                boolean haveHadUnqualifiedParameter = false;
483                for (QualifiedParamList paramList : theParameters) {
484                        if (paramList.size() == 0) {
485                                continue;
486                        }
487                        if (paramList.size() > 1) {
488                                throw new InvalidRequestException("DateRange parameter does not support OR queries");
489                        }
490                        String param = paramList.get(0);
491
492                        /*
493                         * Since ' ' is escaped as '+' we'll be nice to anyone might have accidentally not
494                         * escaped theirs
495                         */
496                        param = param.replace(' ', '+');
497
498                        DateParam parsed = new DateParam();
499                        parsed.setValueAsQueryToken(theContext, theParamName, paramList.getQualifier(), param);
500                        addParam(parsed);
501
502                        if (parsed.getPrefix() == null) {
503                                if (haveHadUnqualifiedParameter) {
504                                        throw new InvalidRequestException("Multiple date parameters with the same name and no qualifier (>, <, etc.) is not supported");
505                                }
506                                haveHadUnqualifiedParameter = true;
507                        }
508
509                }
510
511        }
512
513        @Override
514        public String toString() {
515                StringBuilder b = new StringBuilder();
516                b.append(getClass().getSimpleName());
517                b.append("[");
518                if (hasBound(myLowerBound)) {
519                        if (myLowerBound.getPrefix() != null) {
520                                b.append(myLowerBound.getPrefix().getValue());
521                        }
522                        b.append(myLowerBound.getValueAsString());
523                }
524                if (hasBound(myUpperBound)) {
525                        if (hasBound(myLowerBound)) {
526                                b.append(" ");
527                        }
528                        if (myUpperBound.getPrefix() != null) {
529                                b.append(myUpperBound.getPrefix().getValue());
530                        }
531                        b.append(myUpperBound.getValueAsString());
532                } else {
533                        if (!hasBound(myLowerBound)) {
534                                b.append("empty");
535                        }
536                }
537                b.append("]");
538                return b.toString();
539        }
540
541        /**
542         * Note: An operation can take a DateRangeParam. If only a single date is provided,
543         * it will still result in a DateRangeParam where the lower and upper bounds
544         * are the same value. As such, even though the prefixes for the lower and
545         * upper bounds default to <code>ge</code> and <code>le</code> respectively,
546         * the resulting prefix is effectively <code>eq</code> where only a single
547         * date is provided - as required by the FHIR specificiation (i.e. "If no
548         * prefix is present, the prefix <code>eq</code> is assumed").
549         */
550        private void validateAndSet(DateParam lowerBound, DateParam upperBound) {
551                if (hasBound(lowerBound) && hasBound(upperBound)) {
552                        if (lowerBound.getValue().getTime() > upperBound.getValue().getTime()) {
553                                throw new DataFormatException(format(
554                                        "Lower bound of %s is after upper bound of %s",
555                                        lowerBound.getValueAsString(), upperBound.getValueAsString()));
556                        }
557                }
558
559                if (hasBound(lowerBound)) {
560                        if (lowerBound.getPrefix() == null) {
561                                lowerBound.setPrefix(GREATERTHAN_OR_EQUALS);
562                        }
563                        switch (lowerBound.getPrefix()) {
564                                case GREATERTHAN:
565                                case GREATERTHAN_OR_EQUALS:
566                                default:
567                                        break;
568                                case LESSTHAN:
569                                case LESSTHAN_OR_EQUALS:
570                                        throw new DataFormatException("Lower bound comparator must be > or >=, can not be " + lowerBound.getPrefix().getValue());
571                        }
572                }
573
574                if (hasBound(upperBound)) {
575                        if (upperBound.getPrefix() == null) {
576                                upperBound.setPrefix(LESSTHAN_OR_EQUALS);
577                        }
578                        switch (upperBound.getPrefix()) {
579                                case LESSTHAN:
580                                case LESSTHAN_OR_EQUALS:
581                                default:
582                                        break;
583                                case GREATERTHAN:
584                                case GREATERTHAN_OR_EQUALS:
585                                        throw new DataFormatException("Upper bound comparator must be < or <=, can not be " + upperBound.getPrefix().getValue());
586                        }
587                }
588
589                myLowerBound = lowerBound;
590                myUpperBound = upperBound;
591        }
592
593}