1 package com.atlassian.user.impl.hibernate.search.query;
2
3 import com.atlassian.user.Entity;
4 import com.atlassian.user.EntityException;
5 import com.atlassian.user.impl.RepositoryException;
6 import com.atlassian.user.impl.hibernate.DefaultHibernateExternalEntity;
7 import com.atlassian.user.impl.hibernate.DefaultHibernateGroup;
8 import com.atlassian.user.impl.hibernate.DefaultHibernateUser;
9 import com.atlassian.user.impl.hibernate.repository.HibernateRepository;
10 import com.atlassian.user.repository.RepositoryIdentifier;
11 import com.atlassian.user.search.DefaultSearchResult;
12 import com.atlassian.user.search.SearchResult;
13 import com.atlassian.user.search.page.DefaultPager;
14 import com.atlassian.user.search.query.*;
15 import net.sf.hibernate.Criteria;
16 import net.sf.hibernate.HibernateException;
17 import net.sf.hibernate.Session;
18 import net.sf.hibernate.expression.*;
19 import org.springframework.orm.hibernate.SessionFactoryUtils;
20
21 import java.util.*;
22
23
24
25
26 public final class HibernateEntityQueryParser implements EntityQueryParser
27 {
28 private final RepositoryIdentifier identifier;
29 private final HibernateRepository repository;
30 private final QueryValidator queryValidator = new QueryValidator();
31
32 public HibernateEntityQueryParser(RepositoryIdentifier identifier, HibernateRepository repository)
33 {
34 this.identifier = identifier;
35 this.repository = repository;
36 }
37
38 public SearchResult findUsers(Query query) throws EntityException
39 {
40 queryValidator.assertValid(query);
41 return parseQuery(query);
42 }
43
44 public SearchResult findGroups(Query query) throws EntityException
45 {
46 queryValidator.assertValid(query);
47 return parseQuery(query);
48 }
49
50 public SearchResult findUsers(Query query, QueryContext context) throws EntityException
51 {
52 if (!context.contains(identifier))
53 return null;
54
55 queryValidator.assertValid(query);
56 return parseQuery(query);
57 }
58
59 public SearchResult findGroups(Query query, QueryContext context) throws EntityException
60 {
61 if (!context.contains(identifier))
62 return null;
63
64 queryValidator.assertValid(query);
65 return parseQuery(query);
66 }
67
68 private MatchMode getMatchMode(String matchingRule)
69 {
70 if (matchingRule.equals(TermQuery.SUBSTRING_CONTAINS))
71 {
72 return MatchMode.ANYWHERE;
73 }
74 if (matchingRule.equals(TermQuery.SUBSTRING_ENDS_WITH))
75 {
76 return MatchMode.END;
77 }
78 if (matchingRule.equals(TermQuery.SUBSTRING_STARTS_WITH))
79 {
80 return MatchMode.START;
81 }
82 return MatchMode.EXACT;
83 }
84
85 private String identifyProperty(TermQuery q)
86 {
87 if (q instanceof UserNameTermQuery)
88 return "name";
89 else if (q instanceof EmailTermQuery)
90 return "email";
91 else if (q instanceof FullNameTermQuery)
92 return "fullName";
93 else if (q instanceof GroupNameTermQuery)
94 return "name";
95 else if (q instanceof GroupsOfUserTwoTermQuery)
96 return "entity";
97 return null;
98 }
99
100
101
102
103
104 private SearchResult parseQuery(Query query) throws EntityException
105 {
106
107 Session session = SessionFactoryUtils.getSession(repository.getSessionFactory(), true);
108 Query definingQuery = (query instanceof BooleanQuery) ? identifyDefiningQuery((BooleanQuery) query) : query;
109
110 List result;
111 try
112 {
113 Criteria baseCriteria = getBaseCriteria(definingQuery, session);
114 baseCriteria = identifyAndAddSearchCriteria(query, definingQuery, baseCriteria);
115 baseCriteria.addOrder(Order.asc("name"));
116
117 result = baseCriteria.list();
118 }
119 catch (HibernateException e)
120 {
121 throw new RepositoryException(e);
122 }
123
124 return new DefaultSearchResult(new DefaultPager(result), identifier.getName());
125 }
126
127 private Criteria identifyAndAddSearchCriteria(Query q, Query definingQuery, Criteria baseCriteria)
128 throws EntityQueryException, HibernateException
129 {
130 if (q instanceof BooleanQuery)
131 return addSearchCriteria((BooleanQuery) q, definingQuery, baseCriteria);
132 else
133 {
134 return addSearchCriteria((TermQuery) q, baseCriteria);
135 }
136 }
137
138 private Criteria addSearchCriteria(BooleanQuery q, Query definingQuery, Criteria baseCriteria)
139 throws EntityQueryException, HibernateException
140 {
141
142
143
144 if (definingQuery instanceof MembershipQuery)
145 {
146 return addMembershipSearchCriteria(q, baseCriteria);
147 }
148
149 Junction junction = identifyAndOrJunction(q);
150 baseCriteria.add(junction);
151
152 for (Query query : q.getQueries())
153 {
154 if (query instanceof BooleanQuery)
155 {
156 addSearchCriteria((BooleanQuery) query, definingQuery, baseCriteria);
157 }
158 else if (query instanceof TermQuery)
159 {
160 junction.add(getQueryExpression((TermQuery) query));
161 }
162 else
163 throw new EntityQueryException("Unknown query type: [" + query.getClass().getName() + "]");
164 }
165
166 return baseCriteria;
167 }
168
169 private Junction identifyAndOrJunction(BooleanQuery q)
170 {
171 Junction junction;
172 if (q.isAND())
173 junction = Expression.conjunction();
174 else
175 junction = Expression.disjunction();
176
177 return junction;
178 }
179
180 private Criteria addMembershipSearchCriteria(BooleanQuery q, Criteria baseCriteria) throws HibernateException
181 {
182 if (q instanceof GroupsOfUserTwoTermQuery)
183 {
184 addGroupsOfUserSearchCriteria(q, baseCriteria);
185 }
186 else if (q instanceof GroupsOfExternalEntityTwoTermQuery)
187 {
188 addGroupsOfExternalEntitySearchCriteria(q, baseCriteria);
189 }
190
191 return baseCriteria;
192 }
193
194 private void addGroupsOfUserSearchCriteria(BooleanQuery q, Criteria baseCriteria) throws HibernateException
195 {
196 UserNameTermQuery userNameQuery = ((GroupsOfUserTwoTermQuery) q).getUserNameTermQuery();
197 GroupNameTermQuery groupNameQuery = ((GroupsOfUserTwoTermQuery) q).getGroupNameTermQuery();
198
199 if (groupNameQuery.getTerm().equals(TermQuery.WILDCARD))
200 {
201
202 }
203 else if (groupNameQuery.isMatchingSubstring())
204 {
205 baseCriteria.add(getLikeExpression("name", groupNameQuery, false));
206 }
207 else
208 {
209 baseCriteria.add(new EqExpression("name", groupNameQuery.getTerm(), false));
210 }
211
212 if (userNameQuery.isMatchingSubstring())
213 {
214 baseCriteria.createCriteria("localMembers").add(getLikeExpression("name", userNameQuery, false));
215 }
216 else
217 {
218 baseCriteria.createCriteria("localMembers").add(new EqExpression("name", userNameQuery.getTerm(), false));
219 }
220 }
221
222 private Criterion getLikeExpression(String entityAttribute, TermQuery termQuery, boolean caseInsensitive)
223 {
224 if (caseInsensitive)
225 return Expression.ilike(entityAttribute, termQuery.getTerm(), getMatchMode(termQuery.getMatchingRule()));
226 else
227 return Expression.like(entityAttribute, termQuery.getTerm(), getMatchMode(termQuery.getMatchingRule()));
228 }
229
230 private void addGroupsOfExternalEntitySearchCriteria(BooleanQuery q, Criteria baseCriteria)
231 throws HibernateException
232 {
233 ExternalEntityNameTermQuery nameQuery =
234 ((GroupsOfExternalEntityTwoTermQuery) q).getExternalEntityNameTermQuery();
235 GroupNameTermQuery groupQuery = ((GroupsOfExternalEntityTwoTermQuery) q).getGroupNameTermQuery();
236
237 if (groupQuery.getTerm().equals(TermQuery.WILDCARD))
238 {
239
240 }
241 else if (groupQuery.isMatchingSubstring())
242 {
243 baseCriteria.add(getLikeExpression("name", groupQuery, false));
244 }
245 else
246 {
247 baseCriteria.add(new EqExpression("name", groupQuery.getTerm(), false));
248 }
249
250 if (nameQuery.isMatchingSubstring())
251 {
252 baseCriteria.createCriteria("externalMembers").add(getLikeExpression("name", nameQuery, false));
253 }
254 else
255 {
256 baseCriteria.createCriteria("externalMembers").add(new EqExpression("name", nameQuery.getTerm(), false));
257 }
258 }
259
260 private Criteria addSearchCriteria(TermQuery q, Criteria baseCriteria)
261 {
262 Criterion expression = getQueryExpression(q);
263 baseCriteria.add(expression);
264 return baseCriteria;
265 }
266
267 private Criterion getQueryExpression(TermQuery termQuery)
268 {
269 String hqlField = identifyProperty(termQuery);
270
271 if (termQuery.isMatchingSubstring())
272 {
273 return getLikeExpression(hqlField, termQuery, true);
274 }
275 else
276 {
277 return new EqExpression(hqlField, termQuery.getTerm(), true);
278 }
279 }
280
281 private static final Map<Class<? extends Query>, Class<? extends Entity>> QUERY_TYPE_CLASSES = new LinkedHashMap<Class<? extends Query>, Class<? extends Entity>>();
282
283 static {
284 QUERY_TYPE_CLASSES.put(UserQuery.class, DefaultHibernateUser.class);
285 QUERY_TYPE_CLASSES.put(GroupQuery.class, DefaultHibernateGroup.class);
286 QUERY_TYPE_CLASSES.put(UsersInGroupTwoTermQuery.class, DefaultHibernateUser.class);
287 QUERY_TYPE_CLASSES.put(GroupsOfUserTwoTermQuery.class, DefaultHibernateGroup.class);
288 QUERY_TYPE_CLASSES.put(GroupsOfExternalEntityTwoTermQuery.class, DefaultHibernateGroup.class);
289 QUERY_TYPE_CLASSES.put(ExternalEntitiesInGroupTwoTermQuery.class, DefaultHibernateExternalEntity.class);
290 }
291
292 private Criteria getBaseCriteria(Query query, Session session)
293 {
294 for (Map.Entry<Class<? extends Query>, Class<? extends Entity>> entry : QUERY_TYPE_CLASSES.entrySet())
295 {
296 if (entry.getKey().isInstance(query))
297 {
298 return session.createCriteria(entry.getValue());
299 }
300 }
301 return null;
302 }
303
304
305
306
307 private Query identifyDefiningQuery(BooleanQuery q)
308 {
309 if (q instanceof MembershipQuery)
310 return q;
311 for (Query query : q.getQueries())
312 {
313 if (query instanceof TermQuery)
314 return query;
315 if (query instanceof BooleanQuery)
316 return identifyDefiningQuery((BooleanQuery) query);
317 }
318 return null;
319 }
320 }