1
2
3
4
5
6 package gov.nist.secauto.metaschema.core.metapath.function;
7
8 import gov.nist.secauto.metaschema.core.metapath.DynamicContext;
9 import gov.nist.secauto.metaschema.core.metapath.function.impl.OperationFunctions;
10 import gov.nist.secauto.metaschema.core.metapath.function.library.FnNot;
11 import gov.nist.secauto.metaschema.core.metapath.item.ISequence;
12 import gov.nist.secauto.metaschema.core.metapath.item.atomic.IAnyAtomicItem;
13 import gov.nist.secauto.metaschema.core.metapath.item.atomic.IBase64BinaryItem;
14 import gov.nist.secauto.metaschema.core.metapath.item.atomic.IBooleanItem;
15 import gov.nist.secauto.metaschema.core.metapath.item.atomic.IDateItem;
16 import gov.nist.secauto.metaschema.core.metapath.item.atomic.IDateTimeItem;
17 import gov.nist.secauto.metaschema.core.metapath.item.atomic.IDayTimeDurationItem;
18 import gov.nist.secauto.metaschema.core.metapath.item.atomic.IDecimalItem;
19 import gov.nist.secauto.metaschema.core.metapath.item.atomic.IDurationItem;
20 import gov.nist.secauto.metaschema.core.metapath.item.atomic.INumericItem;
21 import gov.nist.secauto.metaschema.core.metapath.item.atomic.IStringItem;
22 import gov.nist.secauto.metaschema.core.metapath.item.atomic.ITimeItem;
23 import gov.nist.secauto.metaschema.core.metapath.item.atomic.IUntypedAtomicItem;
24 import gov.nist.secauto.metaschema.core.metapath.item.atomic.IYearMonthDurationItem;
25 import gov.nist.secauto.metaschema.core.metapath.type.InvalidTypeMetapathException;
26
27 import java.util.Locale;
28
29 import edu.umd.cs.findbugs.annotations.NonNull;
30 import edu.umd.cs.findbugs.annotations.Nullable;
31
32
33
34
35
36
37
38
39
40
41 @SuppressWarnings({ "PMD.GodClass", "PMD.CyclomaticComplexity" })
42 public final class ComparisonFunctions {
43
44
45
46 public enum Operator {
47
48
49
50 EQ,
51
52
53
54 NE,
55
56
57
58 LT,
59
60
61
62 LE,
63
64
65
66 GT,
67
68
69
70 GE;
71 }
72
73 private ComparisonFunctions() {
74
75 }
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90 @NonNull
91 public static IBooleanItem valueCompairison(
92 @NonNull IAnyAtomicItem leftItem,
93 @NonNull Operator operator,
94 @NonNull IAnyAtomicItem rightItem,
95 @Nullable DynamicContext dynamicContext) {
96 return compare(leftItem, operator, rightItem, dynamicContext);
97 }
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112 @NonNull
113 public static IBooleanItem generalComparison(
114 @NonNull ISequence<? extends IAnyAtomicItem> leftItems,
115 @NonNull Operator operator,
116 @NonNull ISequence<? extends IAnyAtomicItem> rightItems,
117 @NonNull DynamicContext dynamicContext) {
118
119 IBooleanItem retval = IBooleanItem.FALSE;
120 for (IAnyAtomicItem left : leftItems) {
121 assert left != null;
122 for (IAnyAtomicItem right : rightItems) {
123 assert right != null;
124 IAnyAtomicItem leftCast;
125 IAnyAtomicItem rightCast;
126 if (left instanceof IUntypedAtomicItem) {
127 if (right instanceof IUntypedAtomicItem) {
128 leftCast = IStringItem.cast(left);
129 rightCast = IStringItem.cast(right);
130 } else {
131 leftCast = applyGeneralComparisonCast(right, left);
132 rightCast = right;
133 }
134 } else if (right instanceof IUntypedAtomicItem) {
135 leftCast = left;
136 rightCast = applyGeneralComparisonCast(left, right);
137 } else {
138 leftCast = left;
139 rightCast = right;
140 }
141
142 assert leftCast != null;
143 IBooleanItem result = compare(leftCast, operator, rightCast, dynamicContext);
144 if (IBooleanItem.TRUE.equals(result)) {
145 retval = IBooleanItem.TRUE;
146 }
147 }
148 }
149 return retval;
150 }
151
152
153
154
155
156
157
158
159
160
161
162 @NonNull
163 private static IAnyAtomicItem applyGeneralComparisonCast(
164 @NonNull IAnyAtomicItem item,
165 @NonNull IAnyAtomicItem other) {
166 IAnyAtomicItem retval;
167 if (item instanceof INumericItem) {
168 retval = IDecimalItem.cast(other);
169 } else if (item instanceof IDayTimeDurationItem) {
170 retval = IDayTimeDurationItem.cast(other);
171 } else if (item instanceof IYearMonthDurationItem) {
172 retval = IYearMonthDurationItem.cast(other);
173 } else {
174 retval = item.castAsType(other);
175 }
176 return retval;
177 }
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193 @NonNull
194 public static IBooleanItem compare(
195 @NonNull IAnyAtomicItem left,
196 @NonNull Operator operator,
197 @NonNull IAnyAtomicItem right,
198 @Nullable DynamicContext dynamicContext) {
199 @NonNull
200 IBooleanItem retval;
201 if (left instanceof IStringItem || right instanceof IStringItem) {
202 retval = stringCompare(IStringItem.cast(left), operator, IStringItem.cast(right));
203 } else if (left instanceof INumericItem && right instanceof INumericItem) {
204 retval = numericCompare((INumericItem) left, operator, (INumericItem) right);
205 } else if (left instanceof IBooleanItem && right instanceof IBooleanItem) {
206 retval = booleanCompare((IBooleanItem) left, operator, (IBooleanItem) right);
207 } else if (left instanceof IDateTimeItem && right instanceof IDateTimeItem) {
208 retval = dateTimeCompare((IDateTimeItem) left, operator, (IDateTimeItem) right, dynamicContext);
209 } else if (left instanceof IDateItem && right instanceof IDateItem) {
210 retval = dateTimeCompare(
211 ((IDateItem) left).asDateTime(),
212 operator,
213 ((IDateItem) right).asDateTime(),
214 dynamicContext);
215 } else if (left instanceof ITimeItem && right instanceof ITimeItem) {
216 retval = dateTimeCompare(
217 IDateTimeItem.valueOf(OperationFunctions.referenceDate(), (ITimeItem) left),
218 operator,
219 IDateTimeItem.valueOf(OperationFunctions.referenceDate(), (ITimeItem) right),
220 dynamicContext);
221 } else if (left instanceof IDurationItem && right instanceof IDurationItem) {
222 retval = durationCompare((IDurationItem) left, operator, (IDurationItem) right);
223 } else if (left instanceof IBase64BinaryItem && right instanceof IBase64BinaryItem) {
224 retval = binaryCompare((IBase64BinaryItem) left, operator, (IBase64BinaryItem) right);
225 } else {
226 throw new InvalidTypeMetapathException(
227 null,
228 String.format("invalid types for comparison: %s %s %s", left.getClass().getName(),
229 operator.name().toLowerCase(Locale.ROOT), right.getClass().getName()));
230 }
231 return retval;
232 }
233
234
235
236
237
238
239
240
241
242
243
244
245
246 @NonNull
247 public static IBooleanItem stringCompare(
248 @NonNull IStringItem left,
249 @NonNull Operator operator,
250 @NonNull IStringItem right) {
251 int result = left.compareTo(right);
252 Boolean retval = null;
253 switch (operator) {
254 case EQ:
255 retval = result == 0;
256 break;
257 case GE:
258 retval = result >= 0;
259 break;
260 case GT:
261 retval = result > 0;
262 break;
263 case LE:
264 retval = result <= 0;
265 break;
266 case LT:
267 retval = result < 0;
268 break;
269 case NE:
270 retval = result != 0;
271 break;
272 }
273
274 assert retval != null : String.format("Unsupported operator '%s'", operator.name());
275
276 return IBooleanItem.valueOf(retval);
277 }
278
279
280
281
282
283
284
285
286
287
288
289
290
291 @NonNull
292 public static IBooleanItem numericCompare(@NonNull INumericItem left, @NonNull Operator operator,
293 @NonNull INumericItem right) {
294 IBooleanItem retval = null;
295 switch (operator) {
296 case EQ:
297 retval = OperationFunctions.opNumericEqual(left, right);
298 break;
299 case GE: {
300 IBooleanItem gt = OperationFunctions.opNumericGreaterThan(left, right);
301 IBooleanItem eq = OperationFunctions.opNumericEqual(left, right);
302 retval = IBooleanItem.valueOf(gt.toBoolean() || eq.toBoolean());
303 break;
304 }
305 case GT:
306 retval = OperationFunctions.opNumericGreaterThan(left, right);
307 break;
308 case LE: {
309 IBooleanItem lt = OperationFunctions.opNumericLessThan(left, right);
310 IBooleanItem eq = OperationFunctions.opNumericEqual(left, right);
311 retval = IBooleanItem.valueOf(lt.toBoolean() || eq.toBoolean());
312 break;
313 }
314 case LT:
315 retval = OperationFunctions.opNumericLessThan(left, right);
316 break;
317 case NE:
318 retval = FnNot.fnNot(OperationFunctions.opNumericEqual(left, right));
319 break;
320 }
321 assert retval != null : String.format("Unsupported operator '%s'", operator.name());
322
323 return retval;
324 }
325
326
327
328
329
330
331
332
333
334
335
336
337
338 @NonNull
339 public static IBooleanItem booleanCompare(
340 @NonNull IBooleanItem left,
341 @NonNull Operator operator,
342 @NonNull IBooleanItem right) {
343 IBooleanItem retval = null;
344 switch (operator) {
345 case EQ:
346 retval = OperationFunctions.opBooleanEqual(left, right);
347 break;
348 case GE: {
349 IBooleanItem gt = OperationFunctions.opBooleanGreaterThan(left, right);
350 IBooleanItem eq = OperationFunctions.opBooleanEqual(left, right);
351 retval = IBooleanItem.valueOf(gt.toBoolean() || eq.toBoolean());
352 break;
353 }
354 case GT:
355 retval = OperationFunctions.opBooleanGreaterThan(left, right);
356 break;
357 case LE: {
358 IBooleanItem lt = OperationFunctions.opBooleanLessThan(left, right);
359 IBooleanItem eq = OperationFunctions.opBooleanEqual(left, right);
360 retval = IBooleanItem.valueOf(lt.toBoolean() || eq.toBoolean());
361 break;
362 }
363 case LT:
364 retval = OperationFunctions.opBooleanLessThan(left, right);
365 break;
366 case NE:
367 retval = FnNot.fnNot(OperationFunctions.opBooleanEqual(left, right));
368 break;
369 }
370 assert retval != null : String.format("Unsupported operator '%s'", operator.name());
371
372 return retval;
373 }
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389 @NonNull
390 public static IBooleanItem dateTimeCompare(
391 @NonNull IDateTimeItem left,
392 @NonNull Operator operator,
393 @NonNull IDateTimeItem right,
394 @Nullable DynamicContext dynamicContext) {
395 IBooleanItem retval = null;
396 switch (operator) {
397 case EQ:
398 retval = OperationFunctions.opDateTimeEqual(left, right, dynamicContext);
399 break;
400 case GE: {
401
402 IDateTimeItem leftNormalized = dynamicContext == null ? left : left.normalize(dynamicContext);
403 IDateTimeItem rightNormalized = dynamicContext == null ? right : right.normalize(dynamicContext);
404 retval = IBooleanItem.valueOf(
405 OperationFunctions.opDateTimeGreaterThan(leftNormalized, rightNormalized, null).toBoolean()
406 || OperationFunctions.opDateTimeEqual(leftNormalized, rightNormalized, null).toBoolean());
407 break;
408 }
409 case GT:
410 retval = OperationFunctions.opDateTimeGreaterThan(left, right, dynamicContext);
411 break;
412 case LE: {
413
414 IDateTimeItem leftNormalized = dynamicContext == null ? left : left.normalize(dynamicContext);
415 IDateTimeItem rightNormalized = dynamicContext == null ? right : right.normalize(dynamicContext);
416 retval = IBooleanItem.valueOf(
417 OperationFunctions.opDateTimeLessThan(leftNormalized, rightNormalized, null).toBoolean()
418 || OperationFunctions.opDateTimeEqual(leftNormalized, rightNormalized, null).toBoolean());
419 break;
420 }
421 case LT:
422 retval = OperationFunctions.opDateTimeLessThan(left, right, dynamicContext);
423 break;
424 case NE:
425 retval = FnNot.fnNot(OperationFunctions.opDateTimeEqual(left, right, dynamicContext));
426 break;
427 }
428 assert retval != null : String.format("Unsupported operator '%s'", operator.name());
429
430 return retval;
431 }
432
433
434
435
436
437
438
439
440
441
442
443
444
445 @NonNull
446 public static IBooleanItem durationCompare(
447 @NonNull IDurationItem left,
448 @NonNull Operator operator,
449 @NonNull IDurationItem right) {
450 IBooleanItem retval = null;
451 switch (operator) {
452 case EQ:
453 retval = OperationFunctions.opDurationEqual(left, right);
454 break;
455 case GE:
456 if (left instanceof IYearMonthDurationItem && right instanceof IYearMonthDurationItem) {
457 IBooleanItem gt = OperationFunctions.opYearMonthDurationGreaterThan(
458 (IYearMonthDurationItem) left,
459 (IYearMonthDurationItem) right);
460 IBooleanItem eq = OperationFunctions.opDurationEqual(left, right);
461 retval = IBooleanItem.valueOf(gt.toBoolean() || eq.toBoolean());
462 } else if (left instanceof IDayTimeDurationItem && right instanceof IDayTimeDurationItem) {
463 IBooleanItem gt = OperationFunctions.opDayTimeDurationGreaterThan(
464 (IDayTimeDurationItem) left,
465 (IDayTimeDurationItem) right);
466 IBooleanItem eq = OperationFunctions.opDurationEqual(left, right);
467 retval = IBooleanItem.valueOf(gt.toBoolean() || eq.toBoolean());
468 }
469 break;
470 case GT:
471 if (left instanceof IYearMonthDurationItem && right instanceof IYearMonthDurationItem) {
472 retval = OperationFunctions.opYearMonthDurationGreaterThan(
473 (IYearMonthDurationItem) left,
474 (IYearMonthDurationItem) right);
475 } else if (left instanceof IDayTimeDurationItem && right instanceof IDayTimeDurationItem) {
476 retval = OperationFunctions.opDayTimeDurationGreaterThan(
477 (IDayTimeDurationItem) left,
478 (IDayTimeDurationItem) right);
479 }
480 break;
481 case LE:
482 if (left instanceof IYearMonthDurationItem && right instanceof IYearMonthDurationItem) {
483 IBooleanItem lt = OperationFunctions.opYearMonthDurationLessThan(
484 (IYearMonthDurationItem) left,
485 (IYearMonthDurationItem) right);
486 IBooleanItem eq = OperationFunctions.opDurationEqual(left, right);
487 retval = IBooleanItem.valueOf(lt.toBoolean() || eq.toBoolean());
488 } else if (left instanceof IDayTimeDurationItem && right instanceof IDayTimeDurationItem) {
489 IBooleanItem lt = OperationFunctions.opDayTimeDurationLessThan(
490 (IDayTimeDurationItem) left,
491 (IDayTimeDurationItem) right);
492 IBooleanItem eq = OperationFunctions.opDurationEqual(left, right);
493 retval = IBooleanItem.valueOf(lt.toBoolean() || eq.toBoolean());
494 }
495 break;
496 case LT:
497 if (left instanceof IYearMonthDurationItem && right instanceof IYearMonthDurationItem) {
498 retval = OperationFunctions.opYearMonthDurationLessThan(
499 (IYearMonthDurationItem) left,
500 (IYearMonthDurationItem) right);
501 } else if (left instanceof IDayTimeDurationItem && right instanceof IDayTimeDurationItem) {
502 retval = OperationFunctions.opDayTimeDurationLessThan(
503 (IDayTimeDurationItem) left,
504 (IDayTimeDurationItem) right);
505 }
506 break;
507 case NE:
508 retval = FnNot.fnNot(OperationFunctions.opDurationEqual(left, right));
509 break;
510 }
511
512 if (retval == null) {
513 throw new InvalidTypeMetapathException(
514 null,
515 String.format("The item types '%s' and '%s' are not comparable",
516 left.getClass().getName(),
517 right.getClass().getName()));
518 }
519 return retval;
520 }
521
522
523
524
525
526
527
528
529
530
531
532
533
534 @NonNull
535 public static IBooleanItem binaryCompare(@NonNull IBase64BinaryItem left, @NonNull Operator operator,
536 @NonNull IBase64BinaryItem right) {
537 IBooleanItem retval = null;
538 switch (operator) {
539 case EQ:
540 retval = OperationFunctions.opBase64BinaryEqual(left, right);
541 break;
542 case GE: {
543 IBooleanItem gt = OperationFunctions.opBase64BinaryGreaterThan(left, right);
544 IBooleanItem eq = OperationFunctions.opBase64BinaryEqual(left, right);
545 retval = IBooleanItem.valueOf(gt.toBoolean() || eq.toBoolean());
546 break;
547 }
548 case GT:
549 retval = OperationFunctions.opBase64BinaryGreaterThan(left, right);
550 break;
551 case LE: {
552 IBooleanItem lt = OperationFunctions.opBase64BinaryLessThan(left, right);
553 IBooleanItem eq = OperationFunctions.opBase64BinaryEqual(left, right);
554 retval = IBooleanItem.valueOf(lt.toBoolean() || eq.toBoolean());
555 break;
556 }
557 case LT:
558 retval = OperationFunctions.opBase64BinaryLessThan(left, right);
559 break;
560 case NE:
561 retval = FnNot.fnNot(OperationFunctions.opBase64BinaryEqual(left, right));
562 break;
563 }
564 assert retval != null : String.format("Unsupported operator '%s'", operator.name());
565
566 return retval;
567 }
568 }