1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34 package info.magnolia.rest.delivery.jcr;
35
36 import static java.util.stream.Collectors.joining;
37
38 import java.time.LocalDate;
39 import java.time.ZoneId;
40 import java.time.ZonedDateTime;
41 import java.time.format.DateTimeFormatter;
42 import java.util.Arrays;
43
44
45
46
47 public class FilteringCondition {
48 private static final String ISO_DATE_TIME_REGEX = "^(?:[1-9]\\d{3}-(?:(?:0[1-9]|1[0-2])-(?:0[1-9]|1\\d|2[0-8])|(?:0[13-9]|1[0-2])-(?:29|30)|(?:0[13578]|1[02])-31)|(?:[1-9]\\d(?:0[48]|[2468][048]|[13579][26])|(?:[2468][048]|[13579][26])00)-02-29)T(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d.[0-9]{3}(?:Z|[+-][01]\\d:[0-5]\\d)$";
49 private static final String ISO_DATE_REGEX = "^(?:[1-9]\\d{3}-(?:(?:0[1-9]|1[0-2])-(?:0[1-9]|1\\d|2[0-8])|(?:0[13-9]|1[0-2])-(?:29|30)|(?:0[13578]|1[02])-31)|(?:[1-9]\\d(?:0[48]|[2468][048]|[13579][26])|(?:[2468][048]|[13579][26])00)-02-29)$";
50 private static final String NUMBER_REGEX = "[+-]?(([1-9][0-9]*)|(0))([.,][0-9]+)?";
51 private static final String NAME_PROPERTY = "@name";
52 private static final String PARENT_PROPERTY = "@ancestor";
53
54 static final DateTimeFormatter JCR_OFFSET_DATE_TIME = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSXXX");
55
56 private static final String VALUES_SPLITTER = "\\|";
57
58 private static final String OPERATOR_OPEN = "[";
59 private static final String OPERATOR_CLOSE = "]";
60
61 private final String property;
62 private final Operator operator;
63 private final String[] values;
64
65 public FilteringCondition(String property, String value) {
66
67 int openPosition = property.lastIndexOf(OPERATOR_OPEN);
68 int closePosition = property.lastIndexOf(OPERATOR_CLOSE);
69 this.property = openPosition == -1 ? property : property.substring(0, openPosition);
70 this.operator = openPosition == -1 ? Operator.EQ : Operator.from(property.substring(openPosition + 1, closePosition));
71
72
73 this.values = value.split(VALUES_SPLITTER);
74 }
75
76 public String asSqlString() {
77 return Arrays.stream(values)
78 .map(this::getConditionPattern)
79 .collect(joining(" OR "));
80 }
81
82 private String getConditionPattern(String value) {
83 if (PARENT_PROPERTY.equalsIgnoreCase(property)) {
84 return String.format("ISDESCENDANTNODE('%s')", value);
85 }
86
87 if (NAME_PROPERTY.equalsIgnoreCase(property)) {
88 return String.format("LOWER(NAME(t)) %s '%s'", operator.asSqlString(), value);
89 }
90
91 return String.format("[%s] %s %s", property, operator.asSqlString(), resolveValue(value));
92 }
93
94 private String resolveValue(String value) {
95 if (value.matches(NUMBER_REGEX)) {
96 return value;
97 } else if (value.matches(ISO_DATE_TIME_REGEX)) {
98 return String.format("CAST('%s' AS DATE)", value);
99 } else if (value.matches(ISO_DATE_REGEX)) {
100 DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
101 LocalDate localDate = LocalDate.parse(value, dateTimeFormatter);
102 ZonedDateTime zonedDateTime = localDate.atStartOfDay(ZoneId.systemDefault());
103 String formattedDateTime = zonedDateTime.format(JCR_OFFSET_DATE_TIME);
104 return String.format("CAST('%s' AS DATE)", formattedDateTime);
105 }
106
107 return String.format("'%s'", value);
108 }
109
110 @Override
111 public String toString() {
112 return asSqlString();
113 }
114 }