1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.syncope.common.lib.search;
20
21 import java.util.Map;
22 import java.util.Optional;
23 import java.util.regex.Matcher;
24 import java.util.regex.Pattern;
25 import org.apache.commons.lang3.StringUtils;
26 import org.apache.cxf.jaxrs.ext.search.ConditionType;
27 import org.apache.cxf.jaxrs.ext.search.SearchBean;
28 import org.apache.cxf.jaxrs.ext.search.SearchCondition;
29 import org.apache.cxf.jaxrs.ext.search.SearchParseException;
30 import org.apache.cxf.jaxrs.ext.search.fiql.FiqlParser;
31
32
33
34
35
36
37
38 public class SyncopeFiqlParser<T> extends FiqlParser<T> {
39
40 public static final String IEQ = "=~";
41
42 public static final String NIEQ = "!~";
43
44 public SyncopeFiqlParser(
45 final Class<T> tclass,
46 final Map<String, String> contextProperties) {
47
48 this(tclass, contextProperties, null);
49 }
50
51 public SyncopeFiqlParser(
52 final Class<T> tclass,
53 final Map<String, String> contextProperties,
54 final Map<String, String> beanProperties) {
55
56 super(tclass, contextProperties, beanProperties);
57
58 operatorsMap.put(IEQ, ConditionType.CUSTOM);
59 operatorsMap.put(NIEQ, ConditionType.CUSTOM);
60
61 CONDITION_MAP.put(ConditionType.CUSTOM, IEQ);
62 CONDITION_MAP.put(ConditionType.CUSTOM, NIEQ);
63
64 String comparators = GT + '|' + GE + '|' + LT + '|' + LE + '|' + EQ + '|' + NEQ + '|' + IEQ + '|' + NIEQ;
65 String s1 = "[\\p{ASCII}]+(" + comparators + ')';
66 comparatorsPattern = Pattern.compile(s1);
67 }
68
69 @Override
70 protected ASTNode<T> parseComparison(final String expr) throws SearchParseException {
71 Matcher m = comparatorsPattern.matcher(expr);
72 if (m.find()) {
73 String propertyName = expr.substring(0, m.start(1));
74 String operator = m.group(1);
75 String value = expr.substring(m.end(1));
76 if (StringUtils.isBlank(value)) {
77 throw new SearchParseException("Not a comparison expression: " + expr);
78 }
79
80 String name = getActualSetterName(unwrapSetter(propertyName));
81 return Optional.ofNullable(parseType(propertyName, name, value)).
82 map(typeInfoObject -> new SyncopeComparison(name, operator, typeInfoObject)).
83 orElse(null);
84 }
85
86 throw new SearchParseException("Not a comparison expression: " + expr);
87 }
88
89 private class SyncopeComparison implements ASTNode<T> {
90
91 private final String name;
92
93 private final String operator;
94
95 private final TypeInfoObject tvalue;
96
97 SyncopeComparison(final String name, final String operator, final TypeInfoObject value) {
98 this.name = name;
99 this.operator = operator;
100 this.tvalue = value;
101 }
102
103 @Override
104 public String toString() {
105 return name + ' ' + operator + ' ' + tvalue.getObject()
106 + " (" + tvalue.getObject().getClass().getSimpleName() + ')';
107 }
108
109 @Override
110 public SearchCondition<T> build() throws SearchParseException {
111 String templateName = getSetter(name);
112 T cond = createTemplate(templateName);
113 ConditionType ct = operatorsMap.get(operator);
114
115 if (isPrimitive(cond)) {
116 return new SyncopeFiqlSearchCondition<>(ct, cond);
117 } else {
118 String templateNameLCase = templateName.toLowerCase();
119 return new SyncopeFiqlSearchCondition<>(Map.of(templateNameLCase, ct),
120 Map.of(templateNameLCase, name),
121 Map.of(templateNameLCase, tvalue.getTypeInfo()),
122 cond, operator);
123 }
124 }
125
126 private boolean isPrimitive(final T pojo) {
127 return pojo.getClass().getName().startsWith("java.lang");
128 }
129
130 @SuppressWarnings("unchecked")
131 private T createTemplate(final String setter) throws SearchParseException {
132 try {
133 if (beanspector != null) {
134 beanspector.instantiate().setValue(setter, tvalue.getObject());
135 return beanspector.getBean();
136 } else {
137 SearchBean bean = (SearchBean) conditionClass.getDeclaredConstructor().newInstance();
138 bean.set(setter, tvalue.getObject().toString());
139 return (T) bean;
140 }
141 } catch (Throwable e) {
142 throw new SearchParseException(e);
143 }
144 }
145 }
146 }