View Javadoc
1   /*
2    * Portions of this software was developed by employees of the National Institute
3    * of Standards and Technology (NIST), an agency of the Federal Government and is
4    * being made available as a public service. Pursuant to title 17 United States
5    * Code Section 105, works of NIST employees are not subject to copyright
6    * protection in the United States. This software may be subject to foreign
7    * copyright. Permission in the United States and in foreign countries, to the
8    * extent that NIST may hold copyright, to use, copy, modify, create derivative
9    * works, and distribute this software and its documentation without fee is hereby
10   * granted on a non-exclusive basis, provided that this notice and disclaimer
11   * of warranty appears in all copies.
12   *
13   * THE SOFTWARE IS PROVIDED 'AS IS' WITHOUT ANY WARRANTY OF ANY KIND, EITHER
14   * EXPRESSED, IMPLIED, OR STATUTORY, INCLUDING, BUT NOT LIMITED TO, ANY WARRANTY
15   * THAT THE SOFTWARE WILL CONFORM TO SPECIFICATIONS, ANY IMPLIED WARRANTIES OF
16   * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND FREEDOM FROM
17   * INFRINGEMENT, AND ANY WARRANTY THAT THE DOCUMENTATION WILL CONFORM TO THE
18   * SOFTWARE, OR ANY WARRANTY THAT THE SOFTWARE WILL BE ERROR FREE.  IN NO EVENT
19   * SHALL NIST BE LIABLE FOR ANY DAMAGES, INCLUDING, BUT NOT LIMITED TO, DIRECT,
20   * INDIRECT, SPECIAL OR CONSEQUENTIAL DAMAGES, ARISING OUT OF, RESULTING FROM,
21   * OR IN ANY WAY CONNECTED WITH THIS SOFTWARE, WHETHER OR NOT BASED UPON WARRANTY,
22   * CONTRACT, TORT, OR OTHERWISE, WHETHER OR NOT INJURY WAS SUSTAINED BY PERSONS OR
23   * PROPERTY OR OTHERWISE, AND WHETHER OR NOT LOSS WAS SUSTAINED FROM, OR AROSE OUT
24   * OF THE RESULTS OF, OR USE OF, THE SOFTWARE OR SERVICES PROVIDED HEREUNDER.
25   */
26  
27  package gov.nist.secauto.metaschema.core.metapath;
28  
29  import gov.nist.secauto.metaschema.core.metapath.function.OperationFunctions;
30  import gov.nist.secauto.metaschema.core.metapath.function.library.FnNot;
31  import gov.nist.secauto.metaschema.core.metapath.item.atomic.IAnyAtomicItem;
32  import gov.nist.secauto.metaschema.core.metapath.item.atomic.IBase64BinaryItem;
33  import gov.nist.secauto.metaschema.core.metapath.item.atomic.IBooleanItem;
34  import gov.nist.secauto.metaschema.core.metapath.item.atomic.IDateItem;
35  import gov.nist.secauto.metaschema.core.metapath.item.atomic.IDateTimeItem;
36  import gov.nist.secauto.metaschema.core.metapath.item.atomic.IDayTimeDurationItem;
37  import gov.nist.secauto.metaschema.core.metapath.item.atomic.IDurationItem;
38  import gov.nist.secauto.metaschema.core.metapath.item.atomic.IIntegerItem;
39  import gov.nist.secauto.metaschema.core.metapath.item.atomic.INumericItem;
40  import gov.nist.secauto.metaschema.core.metapath.item.atomic.IStringItem;
41  import gov.nist.secauto.metaschema.core.metapath.item.atomic.IYearMonthDurationItem;
42  import gov.nist.secauto.metaschema.core.util.ObjectUtils;
43  
44  import java.util.Locale;
45  
46  import edu.umd.cs.findbugs.annotations.NonNull;
47  
48  /**
49   * A common base class for all comparison nodes, which consist of two
50   * expressions representing the left and right sides of the comparison, and a
51   * comparison operator.
52   */
53  abstract class AbstractComparison // NOPMD - unavoidable
54      extends AbstractBinaryExpression<IExpression, IExpression>
55      implements IComparison {
56  
57    @NonNull
58    private final Operator operator;
59  
60    /**
61     * Construct an expression that compares the result of the {@code right}
62     * expression with the result of the {@code left} expression using the specified
63     * {@code operator}.
64     *
65     * @param left
66     *          the expression to compare against
67     * @param operator
68     *          the comparison operator
69     * @param right
70     *          the expression to compare with
71     */
72    public AbstractComparison(@NonNull IExpression left, @NonNull Operator operator, @NonNull IExpression right) {
73      super(left, right);
74      this.operator = ObjectUtils.requireNonNull(operator, "operator");
75    }
76  
77    /**
78     * Get the comparison operator.
79     *
80     * @return the operator
81     */
82    @NonNull
83    public Operator getOperator() {
84      return operator;
85    }
86  
87    @SuppressWarnings("null")
88    @Override
89    public String toASTString() {
90      return String.format("%s[operator=%s]", getClass().getName(), operator);
91    }
92  
93    /**
94     * Compare the {@code right} item with the {@code left} item using the specified
95     * {@code operator}.
96     *
97     * @param left
98     *          the value to compare against
99     * @param operator
100    *          the comparison operator
101    * @param right
102    *          the value to compare with
103    * @return the comparison result
104    */
105   @NonNull
106   protected IBooleanItem compare( // NOPMD - unavoidable
107       @NonNull IAnyAtomicItem left,
108       @NonNull Operator operator,
109       @NonNull IAnyAtomicItem right) {
110     @NonNull IBooleanItem retval;
111     if (left instanceof IStringItem || right instanceof IStringItem) {
112       retval = stringCompare(IStringItem.cast(left), operator, IStringItem.cast(right));
113     } else if (left instanceof INumericItem && right instanceof INumericItem) {
114       retval = numericCompare((INumericItem) left, operator, (INumericItem) right);
115     } else if (left instanceof IBooleanItem && right instanceof IBooleanItem) {
116       retval = booleanCompare((IBooleanItem) left, operator, (IBooleanItem) right);
117     } else if (left instanceof IDateTimeItem && right instanceof IDateTimeItem) {
118       retval = dateTimeCompare((IDateTimeItem) left, operator, (IDateTimeItem) right);
119     } else if (left instanceof IDateItem && right instanceof IDateItem) {
120       retval = dateCompare((IDateItem) left, operator, (IDateItem) right);
121     } else if (left instanceof IDurationItem && right instanceof IDurationItem) {
122       retval = durationCompare((IDurationItem) left, operator, (IDurationItem) right);
123     } else if (left instanceof IBase64BinaryItem && right instanceof IBase64BinaryItem) {
124       retval = binaryCompare((IBase64BinaryItem) left, operator, (IBase64BinaryItem) right);
125     } else {
126       throw new InvalidTypeMetapathException(
127           null,
128           String.format("invalid types for comparison: %s %s %s", left.getClass().getName(),
129               operator.name().toLowerCase(Locale.ROOT), right.getClass().getName()));
130     }
131     return retval;
132   }
133 
134   /**
135    * Perform a string-based comparison of the {@code right} item against the
136    * {@code left} item using the specified {@code operator}.
137    *
138    * @param left
139    *          the value to compare against
140    * @param operator
141    *          the comparison operator
142    * @param right
143    *          the value to compare with
144    * @return the comparison result
145    */
146   @NonNull
147   public static IBooleanItem stringCompare(@NonNull IStringItem left, @NonNull Operator operator,
148       @NonNull IStringItem right) {
149     IBooleanItem retval;
150     switch (operator) {
151     case EQ:
152       retval = OperationFunctions.opNumericEqual(left.compare(right), IIntegerItem.ZERO);
153       break;
154     case GE:
155       retval = OperationFunctions.opNumericGreaterThan(left.compare(right), IIntegerItem.NEGATIVE_ONE);
156       break;
157     case GT:
158       retval = OperationFunctions.opNumericGreaterThan(left.compare(right), IIntegerItem.ZERO);
159       break;
160     case LE:
161       retval = OperationFunctions.opNumericLessThan(left.compare(right), IIntegerItem.ONE);
162       break;
163     case LT:
164       retval = OperationFunctions.opNumericLessThan(left.compare(right), IIntegerItem.ZERO);
165       break;
166     case NE:
167       retval = FnNot.fnNot(OperationFunctions.opNumericEqual(left.compare(right), IIntegerItem.ZERO));
168       break;
169     default:
170       throw new IllegalArgumentException(
171           String.format("Unsupported operator '%s'", operator.name())); // NOPMD
172     }
173     return retval;
174   }
175 
176   /**
177    * Perform a number-based comparison of the {@code right} item against the
178    * {@code left} item using the specified {@code operator}.
179    *
180    * @param left
181    *          the value to compare against
182    * @param operator
183    *          the comparison operator
184    * @param right
185    *          the value to compare with
186    * @return the comparison result
187    */
188   @NonNull
189   public static IBooleanItem numericCompare(@NonNull INumericItem left, @NonNull Operator operator,
190       @NonNull INumericItem right) {
191     IBooleanItem retval;
192     switch (operator) {
193     case EQ:
194       retval = OperationFunctions.opNumericEqual(left, right);
195       break;
196     case GE: {
197       IBooleanItem gt = OperationFunctions.opNumericGreaterThan(left, right);
198       IBooleanItem eq = OperationFunctions.opNumericEqual(left, right);
199       retval = IBooleanItem.valueOf(gt.toBoolean() || eq.toBoolean());
200       break;
201     }
202     case GT:
203       retval = OperationFunctions.opNumericGreaterThan(left, right);
204       break;
205     case LE: {
206       IBooleanItem lt = OperationFunctions.opNumericLessThan(left, right);
207       IBooleanItem eq = OperationFunctions.opNumericEqual(left, right);
208       retval = IBooleanItem.valueOf(lt.toBoolean() || eq.toBoolean());
209       break;
210     }
211     case LT:
212       retval = OperationFunctions.opNumericLessThan(left, right);
213       break;
214     case NE:
215       retval = FnNot.fnNot(OperationFunctions.opNumericEqual(left, right));
216       break;
217     default:
218       throw new IllegalArgumentException(String.format("Unsupported operator '%s'", operator.name()));
219     }
220     return retval;
221   }
222 
223   /**
224    * Perform a boolean-based comparison of the {@code right} item against the
225    * {@code left} item using the specified {@code operator}.
226    *
227    * @param left
228    *          the value to compare against
229    * @param operator
230    *          the comparison operator
231    * @param right
232    *          the value to compare with
233    * @return the comparison result
234    */
235   @NonNull
236   public static IBooleanItem booleanCompare(@NonNull IBooleanItem left, @NonNull Operator operator,
237       @NonNull IBooleanItem right) {
238     IBooleanItem retval;
239     switch (operator) {
240     case EQ:
241       retval = OperationFunctions.opBooleanEqual(left, right);
242       break;
243     case GE: {
244       IBooleanItem gt = OperationFunctions.opBooleanGreaterThan(left, right);
245       IBooleanItem eq = OperationFunctions.opBooleanEqual(left, right);
246       retval = IBooleanItem.valueOf(gt.toBoolean() || eq.toBoolean());
247       break;
248     }
249     case GT:
250       retval = OperationFunctions.opBooleanGreaterThan(left, right);
251       break;
252     case LE: {
253       IBooleanItem lt = OperationFunctions.opBooleanLessThan(left, right);
254       IBooleanItem eq = OperationFunctions.opBooleanEqual(left, right);
255       retval = IBooleanItem.valueOf(lt.toBoolean() || eq.toBoolean());
256       break;
257     }
258     case LT:
259       retval = OperationFunctions.opBooleanLessThan(left, right);
260       break;
261     case NE:
262       retval = FnNot.fnNot(OperationFunctions.opBooleanEqual(left, right));
263       break;
264     default:
265       throw new IllegalArgumentException(String.format("Unsupported operator '%s'", operator.name()));
266     }
267     return retval;
268   }
269 
270   /**
271    * Perform a date and time-based comparison of the {@code right} item against
272    * the {@code left} item using the specified {@code operator}.
273    *
274    * @param left
275    *          the value to compare against
276    * @param operator
277    *          the comparison operator
278    * @param right
279    *          the value to compare with
280    * @return the comparison result
281    */
282   @NonNull
283   public static IBooleanItem dateTimeCompare(@NonNull IDateTimeItem left, @NonNull Operator operator,
284       @NonNull IDateTimeItem right) {
285     IBooleanItem retval;
286     switch (operator) {
287     case EQ:
288       retval = OperationFunctions.opDateTimeEqual(left, right);
289       break;
290     case GE: {
291       IBooleanItem gt = OperationFunctions.opDateTimeGreaterThan(left, right);
292       IBooleanItem eq = OperationFunctions.opDateTimeEqual(left, right);
293       retval = IBooleanItem.valueOf(gt.toBoolean() || eq.toBoolean());
294       break;
295     }
296     case GT:
297       retval = OperationFunctions.opDateTimeGreaterThan(left, right);
298       break;
299     case LE: {
300       IBooleanItem lt = OperationFunctions.opDateTimeLessThan(left, right);
301       IBooleanItem eq = OperationFunctions.opDateTimeEqual(left, right);
302       retval = IBooleanItem.valueOf(lt.toBoolean() || eq.toBoolean());
303       break;
304     }
305     case LT:
306       retval = OperationFunctions.opDateTimeLessThan(left, right);
307       break;
308     case NE:
309       retval = FnNot.fnNot(OperationFunctions.opDateTimeEqual(left, right));
310       break;
311     default:
312       throw new IllegalArgumentException(String.format("Unsupported operator '%s'", operator.name()));
313     }
314     return retval;
315   }
316 
317   /**
318    * Perform a date-based comparison of the {@code right} item against the
319    * {@code left} item using the specified {@code operator}.
320    *
321    * @param left
322    *          the value to compare against
323    * @param operator
324    *          the comparison operator
325    * @param right
326    *          the value to compare with
327    * @return the comparison result
328    */
329   @NonNull
330   public static IBooleanItem dateCompare(@NonNull IDateItem left, @NonNull Operator operator,
331       @NonNull IDateItem right) {
332     IBooleanItem retval;
333     switch (operator) {
334     case EQ:
335       retval = OperationFunctions.opDateEqual(left, right);
336       break;
337     case GE: {
338       IBooleanItem gt = OperationFunctions.opDateGreaterThan(left, right);
339       IBooleanItem eq = OperationFunctions.opDateEqual(left, right);
340       retval = IBooleanItem.valueOf(gt.toBoolean() || eq.toBoolean());
341       break;
342     }
343     case GT:
344       retval = OperationFunctions.opDateGreaterThan(left, right);
345       break;
346     case LE: {
347       IBooleanItem lt = OperationFunctions.opDateLessThan(left, right);
348       IBooleanItem eq = OperationFunctions.opDateEqual(left, right);
349       retval = IBooleanItem.valueOf(lt.toBoolean() || eq.toBoolean());
350       break;
351     }
352     case LT:
353       retval = OperationFunctions.opDateLessThan(left, right);
354       break;
355     case NE:
356       retval = FnNot.fnNot(OperationFunctions.opDateEqual(left, right));
357       break;
358     default:
359       throw new IllegalArgumentException(String.format("Unsupported operator '%s'", operator.name()));
360     }
361     return retval;
362   }
363 
364   /**
365    * Perform a duration-based comparison of the {@code right} item against the
366    * {@code left} item using the specified {@code operator}.
367    *
368    * @param left
369    *          the value to compare against
370    * @param operator
371    *          the comparison operator
372    * @param right
373    *          the value to compare with
374    * @return the comparison result
375    */
376   @NonNull
377   public static IBooleanItem durationCompare( // NOPMD - unavoidable
378       @NonNull IDurationItem left,
379       @NonNull Operator operator,
380       @NonNull IDurationItem right) {
381     IBooleanItem retval = null;
382     switch (operator) {
383     case EQ:
384       retval = OperationFunctions.opDurationEqual(left, right);
385       break;
386     case GE:
387       if (left instanceof IYearMonthDurationItem && right instanceof IYearMonthDurationItem) {
388         IBooleanItem gt = OperationFunctions.opYearMonthDurationGreaterThan(
389             (IYearMonthDurationItem) left,
390             (IYearMonthDurationItem) right);
391         IBooleanItem eq = OperationFunctions.opDurationEqual(left, right);
392         retval = IBooleanItem.valueOf(gt.toBoolean() || eq.toBoolean());
393       } else if (left instanceof IDayTimeDurationItem && right instanceof IDayTimeDurationItem) {
394         IBooleanItem gt = OperationFunctions.opDayTimeDurationGreaterThan(
395             (IDayTimeDurationItem) left,
396             (IDayTimeDurationItem) right);
397         IBooleanItem eq = OperationFunctions.opDurationEqual(left, right);
398         retval = IBooleanItem.valueOf(gt.toBoolean() || eq.toBoolean());
399       }
400       break;
401     case GT:
402       if (left instanceof IYearMonthDurationItem && right instanceof IYearMonthDurationItem) {
403         retval = OperationFunctions.opYearMonthDurationGreaterThan(
404             (IYearMonthDurationItem) left,
405             (IYearMonthDurationItem) right);
406       } else if (left instanceof IDayTimeDurationItem && right instanceof IDayTimeDurationItem) {
407         retval = OperationFunctions.opDayTimeDurationGreaterThan(
408             (IDayTimeDurationItem) left,
409             (IDayTimeDurationItem) right);
410       }
411       break;
412     case LE:
413       if (left instanceof IYearMonthDurationItem && right instanceof IYearMonthDurationItem) {
414         IBooleanItem lt = OperationFunctions.opYearMonthDurationLessThan(
415             (IYearMonthDurationItem) left,
416             (IYearMonthDurationItem) right);
417         IBooleanItem eq = OperationFunctions.opDurationEqual(left, right);
418         retval = IBooleanItem.valueOf(lt.toBoolean() || eq.toBoolean());
419       } else if (left instanceof IDayTimeDurationItem && right instanceof IDayTimeDurationItem) {
420         IBooleanItem lt = OperationFunctions.opDayTimeDurationLessThan(
421             (IDayTimeDurationItem) left,
422             (IDayTimeDurationItem) right);
423         IBooleanItem eq = OperationFunctions.opDurationEqual(left, right);
424         retval = IBooleanItem.valueOf(lt.toBoolean() || eq.toBoolean());
425       }
426       break;
427     case LT:
428       if (left instanceof IYearMonthDurationItem && right instanceof IYearMonthDurationItem) {
429         retval = OperationFunctions.opYearMonthDurationLessThan(
430             (IYearMonthDurationItem) left,
431             (IYearMonthDurationItem) right);
432       } else if (left instanceof IDayTimeDurationItem && right instanceof IDayTimeDurationItem) {
433         retval = OperationFunctions.opDayTimeDurationLessThan(
434             (IDayTimeDurationItem) left,
435             (IDayTimeDurationItem) right);
436       }
437       break;
438     case NE:
439       retval = FnNot.fnNot(OperationFunctions.opDurationEqual(left, right));
440       break;
441     default:
442       throw new IllegalArgumentException(String.format("Unsupported operator '%s'", operator.name()));
443     }
444 
445     if (retval == null) {
446       throw new InvalidTypeMetapathException(
447           null,
448           String.format("The item types '%s' and '%s' are not comparable",
449               left.getClass().getName(),
450               right.getClass().getName()));
451     }
452     return retval;
453   }
454 
455   /**
456    * Perform a binary data-based comparison of the {@code right} item against the
457    * {@code left} item using the specified {@code operator}.
458    *
459    * @param left
460    *          the value to compare against
461    * @param operator
462    *          the comparison operator
463    * @param right
464    *          the value to compare with
465    * @return the comparison result
466    */
467   @NonNull
468   public static IBooleanItem binaryCompare(@NonNull IBase64BinaryItem left, @NonNull Operator operator,
469       @NonNull IBase64BinaryItem right) {
470     IBooleanItem retval;
471     switch (operator) {
472     case EQ:
473       retval = OperationFunctions.opBase64BinaryEqual(left, right);
474       break;
475     case GE: {
476       IBooleanItem gt = OperationFunctions.opBase64BinaryGreaterThan(left, right);
477       IBooleanItem eq = OperationFunctions.opBase64BinaryEqual(left, right);
478       retval = IBooleanItem.valueOf(gt.toBoolean() || eq.toBoolean());
479       break;
480     }
481     case GT:
482       retval = OperationFunctions.opBase64BinaryGreaterThan(left, right);
483       break;
484     case LE: {
485       IBooleanItem lt = OperationFunctions.opBase64BinaryLessThan(left, right);
486       IBooleanItem eq = OperationFunctions.opBase64BinaryEqual(left, right);
487       retval = IBooleanItem.valueOf(lt.toBoolean() || eq.toBoolean());
488       break;
489     }
490     case LT:
491       retval = OperationFunctions.opBase64BinaryLessThan(left, right);
492       break;
493     case NE:
494       retval = FnNot.fnNot(OperationFunctions.opBase64BinaryEqual(left, right));
495       break;
496     default:
497       throw new IllegalArgumentException(String.format("Unsupported operator '%s'", operator.name()));
498     }
499     return retval;
500   }
501 }