1   package com.atlassian.user.search.query;
2   
3   import com.atlassian.user.*;
4   import com.atlassian.user.configuration.Configuration;
5   import com.atlassian.user.configuration.ConfigurationException;
6   import com.atlassian.user.configuration.util.InitializationCheck;
7   import com.atlassian.user.repository.RepositoryIdentifier;
8   import com.atlassian.user.search.DefaultSearchResult;
9   import com.atlassian.user.search.SearchResult;
10  import com.atlassian.user.search.query.match.*;
11  import com.atlassian.user.search.page.DefaultPager;
12  import com.atlassian.user.search.page.Pager;
13  import com.atlassian.user.search.page.PagerUtils;
14  import org.apache.commons.lang.StringUtils;
15  import org.apache.log4j.Logger;
16  
17  import java.lang.reflect.InvocationTargetException;
18  import java.lang.reflect.Method;
19  import java.util.*;
20  
21  /**
22   * This {@link EntityQueryParser} is not efficient, as it handles all queries by loading
23   * user and group {@link Entity} objects into memory.
24   */
25  public class DefaultEntityQueryParser implements EntityQueryParser
26  {
27      private static final Logger log = Logger.getLogger(DefaultEntityQueryParser.class);
28      private final QueryValidator queryValidator = new QueryValidator();
29  
30      protected UserManager userManager;
31      protected GroupManager groupManager;
32      protected RepositoryIdentifier repository;
33      protected Method entityNameMethod;
34      protected Method emailMethod;
35      protected Method fullnameMethod;
36      private static final Class<User> userClass = User.class;
37  
38      public DefaultEntityQueryParser(RepositoryIdentifier repo, UserManager userManager, GroupManager groupManager)
39      {
40          try
41          {
42              entityNameMethod = userClass.getMethod("getName");
43              emailMethod = userClass.getMethod("getEmail");
44              fullnameMethod = userClass.getMethod("getFullName");
45          }
46          catch (NoSuchMethodException e)
47          {
48              log.error(e.getMessage());
49          }
50  
51          this.userManager = userManager;
52          this.groupManager = groupManager;
53          this.repository = repo;
54      }
55  
56      public void init(HashMap args) throws ConfigurationException
57      {
58          this.userManager = (UserManager) args.get(Configuration.USERMANAGER);
59          this.groupManager = (GroupManager) args.get(Configuration.GROUPMANAGER);
60          this.repository = (RepositoryIdentifier) args.get(Configuration.REPOSITORY);
61  
62          InitializationCheck.validateArgs(args, new String[]{Configuration.USERMANAGER,
63              Configuration.GROUPMANAGER,
64              Configuration.REPOSITORY}, this);
65  
66          try
67          {
68              entityNameMethod = userClass.getMethod("getName");
69              emailMethod = userClass.getMethod("getEmail");
70              fullnameMethod = userClass.getMethod("getFullName");
71          }
72          catch (NoSuchMethodException e)
73          {
74              log.error(e.getMessage());
75          }
76      }
77  
78      protected Pager<? extends Entity> parseQuery(Method userMethod, TermQuery q, Pager<? extends Entity> data) throws IllegalAccessException, InvocationTargetException
79      {
80          String searchTerm = StringUtils.defaultString(q.getTerm()).toLowerCase(); // to allow case insensitive search
81          if (searchTerm.indexOf(TermQuery.WILDCARD) >= 0)
82              return data;
83  
84          Matcher matcher;
85          if (q.isMatchingSubstring())
86          {
87              if (q.getMatchingRule().equals(TermQuery.SUBSTRING_STARTS_WITH))
88              {
89                  matcher = new StartsWithIgnoreCaseMatcher();
90              }
91              else if (q.getMatchingRule().equals(TermQuery.SUBSTRING_ENDS_WITH))
92              {
93                  matcher = new EndsWithIgnoreCaseMatcher();
94              }
95              else
96              {
97                  matcher = new ContainsIgnoreCaseMatcher();
98              }
99          }
100         else
101         {
102             matcher = new EqualsIgnoreCaseMatcher();
103         }
104 
105         List<Entity> matches = new ArrayList<Entity>();
106 
107         for (Entity entity : data)
108         {
109             String userInfo = (String) userMethod.invoke(entity);
110 
111             if (matcher.matches(userInfo, searchTerm))
112                 matches.add(entity);
113         }
114         return new DefaultPager<Entity>(matches);
115     }
116 
117     public Pager find(Query query) throws EntityException
118     {
119         Pager<? extends Entity> result = null;
120 
121         if (query instanceof TermQuery)
122         {
123             try
124             {
125                 if (query instanceof UserNameTermQuery)
126                     result = parseQuery(entityNameMethod, (TermQuery) query, userManager.getUsers());
127                 else if (query instanceof GroupNameTermQuery)
128                     result = parseQuery(entityNameMethod, (TermQuery) query, groupManager.getGroups());
129                 else if (query instanceof EmailTermQuery)
130                     result = parseQuery(emailMethod, (TermQuery) query, userManager.getUsers());
131                 else if (query instanceof FullNameTermQuery)
132                     result = parseQuery(fullnameMethod, (TermQuery) query, userManager.getUsers());
133             }
134             catch (IllegalAccessException e)
135             {
136                 throw new EntityException(e);
137             }
138             catch (InvocationTargetException e)
139             {
140                 throw new EntityException(e);
141             }
142         }
143         else if (query instanceof MembershipQuery)
144         {
145             result = parseMembershipQuery(query);
146         }
147         else if (query instanceof BooleanQuery)
148         {
149             BooleanQuery bQuery = (BooleanQuery) query;
150             result = evaluateBoolean(bQuery);
151         }
152 
153         return result;
154     }
155 
156     private Pager<? extends Entity> parseMembershipQuery(Query query) throws EntityException
157     {
158         Pager<? extends Entity> result = null;
159 
160         if (query instanceof UsersInGroupTwoTermQuery)
161         {
162             result = parseUsersInGroupTwoTermQuery((UsersInGroupTwoTermQuery) query);
163         }
164         else if (query instanceof GroupsOfUserTwoTermQuery)
165         {
166             result = parseGroupsOfUserTwoTermQuery((GroupsOfUserTwoTermQuery) query);
167         }
168 
169 //        TermQuery tQuery = (TermQuery) query;
170 //
171 //        if (tQuery.isMatchingSubstring())
172 //        {
173 //            GroupNameTermQuery q = new GroupNameTermQuery(tQuery.getTerm(), tQuery.getMatchingRule());
174 //            Pager pager = find(q);
175 //            Iterator iter = pager.iterator();
176 //
177 //            if (pager.isEmpty())
178 //                return DefaultPager.emptyPager();
179 //            else
180 //            {
181 //                Pager members = null;
182 //                while (iter.hasNext())
183 //                {
184 //                    Group group = (Group) iter.next();
185 //
186 //                    if (members == null)
187 //                        members = groupManager.getMemberNames(group);
188 //                    else
189 //                        members = new MergedPager(members, groupManager.getMemberNames(group));
190 //                }
191 //
192 //                return members;
193 //            }
194 //
195 //        }
196 //        else
197 //            return groupManager.getMemberNames(groupManager.getGroup(tQuery.getTerm()));
198 
199         return result;
200     }
201 
202     private Pager parseUsersInGroupTwoTermQuery(UsersInGroupTwoTermQuery query) throws EntityException
203     {
204         UserNameTermQuery userQuery = query.getUserNameTermQuery();
205         GroupNameTermQuery groupQuery = query.getGroupNameTermQuery();
206 
207         Pager<User> users = find(userQuery);
208         Pager<Group> groups = find(groupQuery);
209 
210         Iterator groupsIter = groups.iterator();
211         Set<String> candidateUsers = new HashSet<String>();
212 
213         while (groupsIter.hasNext())
214         {
215             Group group = (Group) groupsIter.next();
216             candidateUsers.addAll(PagerUtils.toList(groupManager.getLocalMemberNames(group)));
217         }
218 
219         List userQueryList = PagerUtils.toList(users);
220 
221         userQueryList.retainAll(candidateUsers);
222 
223         return new DefaultPager(userQueryList);
224     }
225 
226     private Pager<? extends Entity> evaluateBoolean(BooleanQuery query) throws EntityException
227     {
228         List queries = query.getQueries();
229         Pager<? extends Entity> allResults = null;
230 
231         if (query instanceof MembershipQuery)
232         {
233             return parseMembershipQuery(query);
234         }
235 
236         boolean anding = query.isAND();
237 
238         for (int i = 0; i < queries.size(); i++)
239         {
240             Query nextQuery = (Query) queries.get(i);
241             List initialResult;
242 
243             try
244             {
245                 if (allResults == null)
246                 {
247                     allResults = find(nextQuery);
248                 }
249                 else if (nextQuery instanceof BooleanQuery)
250                 {
251                     if (anding)
252                     {
253                         Pager<? extends Entity> resultsToAnd = evaluateBoolean((BooleanQuery) nextQuery);
254                         List<? extends Entity> allResultsList = PagerUtils.toList(allResults);
255 
256                         List<Entity> resultsToAndList = new ArrayList<Entity>(PagerUtils.toList(resultsToAnd));
257                         resultsToAndList.retainAll(allResultsList);
258                         allResults = new DefaultPager<Entity>(resultsToAndList);
259                     }
260                     else
261                     {
262                         Pager<? extends Entity> resultsToOr = evaluateBoolean((BooleanQuery) nextQuery);
263                         List<Entity> resultsToOrList = new ArrayList<Entity>(PagerUtils.toList(resultsToOr));
264                         List<Entity> intersection = findIntersection(PagerUtils.toList(allResults), resultsToOrList);
265                         allResults = new DefaultPager<Entity>(intersection);
266                     }
267                 }
268                 else if (anding)
269                 {
270 
271                     if (nextQuery instanceof UserNameTermQuery)
272                     {
273                         initialResult = PagerUtils.toList(parseQuery(entityNameMethod, (TermQuery) nextQuery,
274                             allResults));
275                         initialResult.addAll(PagerUtils.toList(allResults));
276                         allResults = new DefaultPager(initialResult);
277                     }
278                     else if (nextQuery instanceof GroupNameTermQuery)
279                     {
280                         initialResult = PagerUtils.toList(parseQuery(entityNameMethod, (TermQuery) nextQuery,
281                             allResults));
282                         initialResult.addAll(PagerUtils.toList(allResults));
283                         allResults = new DefaultPager(initialResult);
284                     }
285                     else if (nextQuery instanceof EmailTermQuery)
286                     {
287                         initialResult = PagerUtils.toList(parseQuery(emailMethod, (TermQuery) nextQuery, allResults));
288                         initialResult.addAll(PagerUtils.toList(allResults));
289                         allResults = new DefaultPager(initialResult);
290                     }
291                     else if (nextQuery instanceof FullNameTermQuery)
292                     {
293                         initialResult = PagerUtils.toList(parseQuery(fullnameMethod, (TermQuery) nextQuery,
294                             allResults));
295                         initialResult.addAll(PagerUtils.toList(allResults));
296                         allResults = new DefaultPager(initialResult);
297                     }
298                 }
299                 else // OR
300                 {
301                     initialResult = PagerUtils.toList(find(nextQuery));
302                     List intersection = findIntersection(PagerUtils.toList(allResults), initialResult);
303                     allResults = new DefaultPager(intersection);
304                 }
305             }
306             catch (Exception e)
307             {
308                 log.error(e.getClass().getName() + " - " + e.getMessage());
309             }
310         }
311 
312         return allResults;
313     }
314 
315     private <T> List<T> findIntersection(List<? extends T> list1, List<? extends T> list2)
316     {
317         List<T> result = new ArrayList<T>(list1);
318 
319         list2.removeAll(list1);
320         result.addAll(list2);
321 
322         return result;
323     }
324 
325     private Pager parseGroupsOfUserTwoTermQuery(GroupsOfUserTwoTermQuery query) throws EntityException
326     {
327         UserNameTermQuery userQuery = query.getUserNameTermQuery();
328         GroupNameTermQuery groupQuery = query.getGroupNameTermQuery();
329 
330         Pager groups = find(groupQuery);
331         Pager users = find(userQuery);
332 
333         Iterator usersIter = users.iterator();
334         Set<Group> candidateGroups = new HashSet<Group>();
335 
336         while (usersIter.hasNext())
337         {
338             User user = (User) usersIter.next();
339             candidateGroups.addAll(PagerUtils.toList(groupManager.getGroups(user)));
340         }
341 
342         List groupQueryList = PagerUtils.toList(groups);
343 
344         groupQueryList.retainAll(candidateGroups);
345 
346         return new DefaultPager(groupQueryList);
347     }
348 
349     public SearchResult findUsers(Query query) throws EntityException
350     {
351         queryValidator.assertValid(query);
352         Pager pager = find(query);
353         return new DefaultSearchResult(pager, repository.getKey());
354     }
355 
356     public SearchResult findGroups(Query query) throws EntityException
357     {
358         queryValidator.assertValid(query);
359         Pager pager = find(query);
360         return new DefaultSearchResult(pager, repository.getKey());
361     }
362 
363     public SearchResult findUsers(Query query, QueryContext context) throws EntityException
364     {
365         if (!context.contains(repository))
366             return null;
367 
368         queryValidator.assertValid(query);
369         return findUsers(query);
370     }
371 
372     public SearchResult findGroups(Query query, QueryContext context) throws EntityException
373     {
374         if (!context.contains(repository))
375             return null;
376 
377         queryValidator.assertValid(query);
378         return findGroups(query);
379     }
380 }